As I was working my way through the chapter on memory management in Understanding the Linux Kernel I thought it would be fun to try to write a program that translates a virtual memory address to a physical address. Moreover, I wanted to do it user space. And to go one step further, why not try to get the physical address of a buffer, go to that location in memory, modify it, and then see the changes by using the virtual address.
WARNING: I am far from a kernel expert. Everything here is me just documenting my experimentation with the kernel. It is very likely that mistakes and incorrect information are present. Please email me with any corrections.
There are a few problems with trying to accomplish this task in user space:
- The idea behind virtual memory is to provide an address space of contiguous memory. The memory for a process is most likely stored in non-contiguous blocks.
- There’s no guarentee that a page is even in the physical memory of the system. It could be in the swap or in a cache somewhere. There could be no physical address to get!
- For obvious security reasons, a process does not have access to the raw memory of the system, even if the process’s UID is 0.
There’s two approaches we can take to get the physical address:
- Add a syscall to the kernel that, given a virtual address, will return the physical address. However, modifying the kernel breaks the rule of doing everything from user space so we have to rule this out.
- Use the pagemap file for a process (added in kernel 2.6.25) to get the frame a page is mapped to and then use that to seek into
/dev/mem and modify the buffer there.
Using this approach, it’s entirely possible to translate a virtual address to a physical address in user space. However, verfying our translation is correct requires reading
/dev/mem. This does require one small modifcation to the kernel (changing a config option), but more on that later.