views:

271

answers:

5

When working sometimes ago on an embedded system with a simple MMU, I used to program dynamically this MMU to detect memory corruptions.

For instance, at some moment at runtime, the foo variable was overwritten with some unexpected data (probably by a dangling pointer or whatever). So I added the additional debugging code :

  • at init, the memory used by foo was indicated as a forbidden region to the MMU;
  • each time foo was accessed on purpose, access to the region was allowed just before then forbidden just after;
  • a MMU irq handler was added to dump the master and the address responsible of the violation.

This was actually some kind of watchpoint, but directly self-handled by the code itself.

Now, I would like to reuse the same trick, but on a x86 platform. The problem is that I am very far from understanding how is working the MMU on this platform, and how it is used by Linux, but I wonder if any library/tool/system call already exist to deal with this problem.

Note that I am aware that various tools exist like Valgrind or GDB to manage memory problems, but as far as I know, none of these tools car be dynamically reconfigured by the debugged code.

I am mainly interested for user space under Linux, but any info on kernel mode or under Windows is also welcome!

+3  A: 

You can use the mmap (MAP_ANONYMOUS) and mprotect functions to manipulate the virtual memory system and use the corresponding protection flags. Your variables need to be constrained to a multiple of the system page size of course. Lots of small variables will present a significant overhead.

Of course your application needs to work correctly when managing access rights to the memory regions. You also need to use mmap() instead of malloc for the protected regions.

This is the user space interface layer to the MMU, in a relatively portable fashion.

mmap and mprotect

Yann Ramin
+2  A: 

Two good options:

  • dmalloc is a library that replaces malloc() and free() with extensive debugging versions, capable of using page boundaries to detect memory overruns/underruns, filling allocated and freed memory, leak-checking, and more.
  • valgrind is a memory debugger that allows very precise memory debugging (detecting accurately any out-of-bounds access) at the expense of program speed (programs run substantially slower under it). It can also do leak checking.
Michael E
The poster mentions that the memory address in question is valid, but wishes to control the variable access based on some other criteria (unlocking and locking the variable). dmalloc is incapable of providing this behavior, while valgrind can't do it out of the box.
Yann Ramin
+1  A: 

I think the best you're going to be able to do is to fire off a watchdog thread that keeps a copy of the value and continually compares its copy to the working value. You won't be able to catch exactly when the value is overwritten, but you'll be notified to within whatever granularity you want (i.e., if you set the thread to check every 10ms you'll be notified within 10ms).

TMN
A: 

Electric fence is sort of old, but still maintained and useful. A number of people have used it as the starting point for more complex debugging. Its extremely easy to modify.

I am also a huge fan of Valgrind, but Valgrind isn't available on all platforms.

Tim Post
+1  A: 

The mprotect() system call is what you're after. This lets you change the protections on a memory region.

Memory protection on x86 under Linux is done at the level of a page - 4096 bytes. So you will have to arrange for your protected variable to live on its own page(s), not sharing with any other variables. One way to arrange for this is to use posix_memalign() to allocate the memory for the variable, using 4096 as the alignment and rounding up the size to the next multiple of 4096 (Actually, you can use sysconf(_SC_PAGESIZE) to determine the page size in a portable manner, rather than using a hardcoded value). Another way to is allocate the variable within a union that pads it out to a multiple of the page size, and use the gcc attribute __attribute__ ((aligned (4096)) to align the variable.

In place of your MMU IRQ handler, you simply install a signal handler for the SIGSEGV signal using the sa_sigaction member of the structure passed to the sigaction() function. Your signal handler will be passed a siginfo_t structure as its second argument, which will contain a member sa_addr that has the address of the faulting instruction.

caf