Skip to content

Blog

Virtual memory to Physical memory

We all know that processes running in Linux acts only in virtual address space. So whenever a process wants to access a data (okay datum) it requests CPU for a virtual address. The CPU intern converts it into physical address and fetches the data. It will be nice to have a program that converts virtual address to physical address, won't it?

Linux from 2.5.26 provides a proc interface, pagemap that contains information what we want. Each process has its pagemap at /proc/p_id/pagemap. According to the Documentation it is a binary file contains a sequence of 64-bit words. Each word contains information regarding one virtual page for full virtual address space. Among them bits 0-54 (55-bits) represents the address of the physical frame number (PFN). I think that's all we need. Adding the offset of a variable from virtual page address to the PFN will give us the physical memory address.

WARNING: Don't try to read the pagemap file directly. cat /proc/self/pagemap or vim /proc/p_id/pagemap is not going to return anytime soon.

We'll write a small C program and the let's try to get physical address of a variable used in that C program. As the PFN data will be present only if the data is not moved to swap, lets use mlock() to lock the memory in physical memory.

#include <stdio.h>
#include <sys/mman.h>   /* for mlock() */
#include <stdlib.h>     /* for malloc() */
#include <string.h>     /* for memset() */

/* for getpid() */
#include <sys/types.h>
#include <unistd.h>

#define MEM_LENGTH 1024

int main()
{
    /* Allocate 1024 bytes in heap */
    char *ptr = NULL;
    ptr = malloc(MEM_LENGTH);
    if (!ptr) {
        perror("malloc fails. ");
        return -1;
    }

    /* obtain physical memory */
    memset(ptr, 1, MEM_LENGTH);

    /* lock the allocated memory in RAM */
    mlock(ptr, MEM_LENGTH);

    /* print the pid and vaddr. Thus we can work on him */
    printf("my pid: %d\n\n", getpid());
    printf("virtual address to work: 0x%lx\n", (unsigned long)ptr);

    /* make the program to wait for user input */
    scanf("%c", &ptr[16]);

    return 0;
}

Run the specimen.c program, get its p_id and start the dissection.

$ gcc specimen.c -o specimen
$ ./specimen
my pid: 11953

virtual address to work: 0x55cd75821260

In a 64-bit machine, virtual address-space is from 0x00 and to 2^64 - 1. First we have to calculate the page offset for the given virtual address [find on which virtual memory page, the address resides]. And multiply that with 8 as each virtual page table has 8-byte information word in the pagemap file.

#define PAGEMAP_LENGTH 8
page_size = getpagesize();
offset = (vaddr page_size) * PAGEMAP_LENGTH;

Open the pagemap file and seek to that offset location.

pagemap = fopen(filename, "rb");
fseek(pagemap, (unsigned long)offset, SEEK_SET)

Now cursor is on the first byte of 64-bit word containing the information we need. According to the Documentation bits 0-54 represents the physical page frame number (PFN). So read 7-bytes and discard most significant bit.

fread(&paddr, 1, (PAGEMAP_LENGTH-1), pagemap)
paddr = paddr & 0x7fffffffffffff;

This is the PFN. Add offset of the virtual address from its virtual page base address to the page shifted PFN to get the physical address of the memory.

offset = vaddr % page_size;
/* PAGE_SIZE = 1U << PAGE_SHIFT */
while (!((1UL << ++page_shift) & page_size));
paddr = (unsigned long)((unsigned long)paddr << page_shift) + offset;

Here is the complete program.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define PAGE_SHIFT 12
#define PAGEMAP_LENGTH 8

int main(int argc, char **argv)
{
    unsigned long vaddr, pid, paddr = 0, offset;
    char *endptr;
    FILE *pagemap;
    char filename[1024] = {0};
    int ret = -1;
    int page_size, page_shift = -1;

    page_size = getpagesize();
    pid = strtol(argv[1], &endptr, 10);
    vaddr = strtol(argv[2], &endptr, 16);
    printf("getting page number of virtual address %lu of process %ld\n",vaddr, pid);

    sprintf(filename, "/proc/%ld/pagemap", pid);

    printf("opening pagemap %s\n", filename);
    pagemap = fopen(filename, "rb");
    if (!pagemap) {
        perror("can't open file. ");
        goto err;
    }

    offset = (vaddr / page_size) * PAGEMAP_LENGTH;
    printf("moving to %ld\n", offset);
    if (fseek(pagemap, (unsigned long)offset, SEEK_SET) != 0) {
        perror("fseek failed. ");
        goto err;
    }

    if (fread(&paddr, 1, (PAGEMAP_LENGTH-1), pagemap) < (PAGEMAP_LENGTH-1)) {
        perror("fread fails. ");
        goto err;
    }
    paddr = paddr & 0x7fffffffffffff;
    printf("physical frame address is 0x%lx\n", paddr);

    offset = vaddr % page_size;

    /* PAGE_SIZE = 1U << PAGE_SHIFT */
    while (!((1UL << ++page_shift) & page_size));

    paddr = (unsigned long)((unsigned long)paddr << page_shift) + offset;
    printf("physical address is 0x%lx\n", paddr);

    ret = 0;
err:
    fclose(pagemap);
    return ret;
}

And the output

$ sudo ./a.out 11953 0x55cd75821260
getting page number of virtual address 94340928115296 of process 11953
opening pagemap /proc/11953/pagemap
moving to 184259625224
physical frame address is 0x20508
physical address is 0x20508260

References

  1. https://www.kernel.org/doc/Documentation/vm/pagemap.txt
  2. https://elixir.bootlin.com/linux/latest/source/arch/x86/include/asm/page_types.h
  3. man pages

Github from Facebook

There are multiple J.A.R.V.I.S projects available in GitHub. All are based on some chat frameworks like api.ai, Alexa, etc. This is yet another rather vaguely intelligent system that I developed for Facebook developer circle challenge hosted at Devpost.

I have developed it just keeping J.A.R.V.I.S in mind. First of all J.A.R.V.I.S is not a simple t-shirt throwing bot like the one Zuckerberg developed. Tony Stark actively uses him during his project development. I mean he is not just a personal assistant but an intelligent co-developer. So I developed one to help you doing your GitHub related activities. You can simply create repos, open close issues, comment on others' issues just from a Facebook chat window. It will save your time and effort from navigating to multiple windows and clicking a dozens of buttons. With voice interface enabled for Facebook chat, he will be your personal J.A.R.V.I.S.

Here is a quick demo.


Code is available at github.com/kaba-official/MO.

VNC setup in Digitalocean

Install xfce desktop environment

$ sudo apt-get install xfce4 xfce4-goodies

Install TightVNC for VNC server

$ sudo apt-get install tightvncserver

Initialize VNC server + run VNC server once + enter and verify password + ignore view-only password

$ vncserver
Password:
Verify:
View-only password:

To configure VNC, first kill the running VNC server

$ vncserver -kill :1
Killing Xtightvnc process ID 13456

Backup xstartup configuration file

$ mv ~/.vnc/xstartup ~/.vnc/xstartup.orig

Edit xstartup file

$ vim ~/.vnc/xstartup

And add following content

*~/.vnc/xstartup*
#!/bin/bash
xrdb $HOME/.Xresources
startxfce4 &
+ Look at ~/.Xresources file for VNC's GUI framework information + Start XFCE whenever VNC server is started

Provide executable permission for ~/.vnc/xstartup file

$ sudo chmod +x ~/.vnc/xstartup

Start VNC server

$ vncserver -geometry 1280x800

Auto deploy from Bitbucket or any other git repository

Update aptitude first

$ sudo apt-get update

Install expect

$ sudo apt-get install expect

Create a directory named trigger in your home directory

$ cd
$ mkdir trigger
$ cd trigger

Create following files inside the trigger directory. trigger.js

$ cat trigger.js
var server_port = <new_port_for_trigger>;

var sys = require('sys');
var exec = require('child_process').exec;
var child;

var http = require('http');
var express = require('express');
var app = express();
var server_get = require('http').Server(app);

app.post('/update', function(req, res) {
    child = exec("./update_repo.sh", function(error, stdout, stderr) {
                console.log('stdout: ' + stdout);
                console.log('stderr: ' + stderr);
                if (error !== null) {
                    console.log('exec error: ' + error);
                }
    });

    res.send("SUCCESS");
});

app.listen(server_port, function() {
    console.log('Example app listening on port ' + server_port);
}

package.json

$ cat package.json
{
    "name": "Trigger",
    "version": "0.0.1",
    "scripts": {
        "start": "node server"
    },
    "dependencies": {
        "express": "^4.14.0",
        "http": "0.0.0",
        "https": "^1.0.0"
    }
}

update_repo.sh

$ cat update_repo.sh
#!/bin/bash
cd <your_git_repo>
~/trigger/git_pull_helper.sh
pm2 restart server

git_pull_helper.sh

$ cat git_pull_helper.sh
#!/usr/bin/expect -f
spawn git pull
expect "ass"
send "<your_ssh_key_pass_phrase>\r"
interact

Update permission for update_repo.sh and git_pull_helper.sh

$ chmod +x update_repo.sh
$ chmod +x git_pull_helper.sh

Start your trigger NodeJS server

$ pm2 start trigger.js

  • Configure you nginx if you are using one
  • Don't forget to enable firewall to allow the new port

Update in bitbucket

  • Got Settings
  • Select Webhooks in Workflow section
  • Click Add webhook button
  • Give name
  • Enter your url as *<your_domain_name>.<ext>:<your_port_for_trigger>/update*
  • Press save button

Or follow the steps provided by your git server.

Add swap space to Ubuntu

Truffle compilation was crashing with memory-overrun issues in my 512 MB basic droplet. So I added 2 GB of swap space with following commands.

Make sure you have no swap config previously. There should be no output.

$ sudo swapon --show
$

The simple way of doing this is by creating a swap file. Lets look at all the partitions to see how much free space in each of them.

$ df -h
kaba@ubuntu-512mb-blr1-01:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            238M     0  238M   0% /dev
tmpfs            49M  5.9M   43M  13% /run
/dev/vda1        20G  5.2G   15G  27% /
tmpfs           245M   24M  221M  10% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           245M     0  245M   0% /sys/fs/cgroup
/dev/vda15      105M  3.4M  102M   4% /boot/efi
tmpfs            49M     0   49M   0% /run/user/0
tmpfs            49M  8.0K   49M   1% /run/user/1000
$
* Other than the ones with /dev/ are virtual filesystem and are not usable. * I'm gonna create my swap file in /dev/vda1.

Use fallocate utility to create a preallocated file in root directory.

$ sudo fallocate -l 2G /swapfile
* As in previous command output, dev/vda1 is mounted on / * 2G specifies 2 GB of space. Use whatever size you want in that place.

Make the is file accessible to root user alone

sudo chmod 600 /swapfile

Mark the file as swap space

$ sudo mkswap /swapfile

Enable the swap file, so your system will start using it

$ sudo swapon /swapfile

Done!

Now swapon command shouldn't return an empty result

$ sudo swapon --show
NAME      TYPE SIZE   USED PRIO
/swapfile file   2G 771.6M   -1


Make swap settings permanent

You need to edit /etc/fstab file. So better take a backup of it.

$ sudo mv /etc/fstab /etc/fstab.orig

Update the file with swap information

$ echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab