So, NJAMD (Not Just Another Malloc Debugger) is just that. It provides memory debugging capabilities simalair to purify. NJAMD replaces the standard system memory allocator with it's own version that performs various checks on your memory usage. Basically it has three main features: 1. It protects against dynamic buffer overflows. Overflows are common mistake made by programmers whereby a buggy program overwrites past the end of a requested region of memory. An example would be: char *buf = malloc(5); // Requests 5 bytes of memory and use the variable // buf to reference it. strcpy(buf, "12345"); // Copy "12345" into the memory at variable buf. // The string "12345" has 6 characters. In C and C++ // strings always end with an invisible "Null" // character, which takes up an extra byte Under normal operation, this type of error can be extremely difficult to track down. The default C library memory allocator malloc devides up system memory and returns the requested length of memory to the user. However, internally the memory allocator stores bookkeeping information about allocated memory before and after the buffer itself. So even this one byte error can cause the memory allocator to become confused much later on during successive memory requests. This can casue all kinds of unpredictable behavior. All because of one byte! Of course, barring not making the mistake in the first place, the most desirable outcome is to have an error generated immediately, so the programmer can discern what is wrong, and can quickly fix it. The way this is accomilished is by telling the operating system to place a section of "protected" memory surrounding each allocation request. However, it is not that simple. Memory can only be protected in multiples called "pages", which are typically 4096 bytes long. So consequently we must properly allign the user's buffer so that it ends on a page boundry. Start of memory obtained from Operating System start of buf Page Boundry \/ \/ \/ |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~/ /~~~||~~~~~~~~~~~~~~~~~~| | offset to align to page boundry| User's memory \ \ || Protected Memory | |________________________________|_______________/ /___||__________________| Now, whenever the user overwrites his memory, even by one byte, he writes to the protected memory, and the operating systems generates what's called a "Segmentation Fault", and kills his process. Using the front end of our utility, (which uses the GNU debugger 'gdb'), the user can discern to the line where in his program this error occured, and fix the error in a matter of seconds, instead of scratching his head wondering why his program appears to work correctly for hours until finnaly the memory allocator corrupts itself due to its confusion. The page of protected memory also forbids reads, this stopping the user's program from reading invalid data outside of it's allocated buffer. 2. In a simalar way, our program protects against underflows. A buffer undeflow is the exact opposite condition. It's where a user reads or writes BEFORE his section. A sample piece of code might look something like: int i; ... i = -1; ... buf[i] = 'J'; This assigns the character J to an memory address 1 byte below the user's buffer. This can also confuse the memory allocator down the line. The way we handle this is to place a section of protected memory before the user's region. This case is much simpler: Start of memory from OS (Page boundry) Page boundry End of User's memory Page boundry \/ \/ \/ \/ |~~~~~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~| | Protected memory || User's memory | Extra padding space | |___________________||_______________|_____________________| 3. A third common mistake made by programmers is to accidentally use memory after it has been returned to the memory allocator by free. Ex: free(buf) // Returns buf to allocator strcpy(buf, "1234"); // copies 5 bytes to buf, but buf doesn't exist! Normally, this would confuse the allocator much like overflows and underflows do. The allocator expects the memory at buf to be available for future allocations, and writing to it may cause you to overwrite your own memory used elsewhere! This solution is very simple, we tell the operating system to put a section of protected memory spanning the entire region of the old memory. Ex: |~~~~~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~| | Protected memory || User's memory | Extra padding space | |___________________||_______________|_____________________| Becomes: |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| | Protected memory | |__________________________________________________________| when the user issues free(buf). That way, any accesses to the memory will immediately result in a segmentation fault. NJAMD does not reuse this memory. However, the memory is not wasted! Modern operating systems do not actually allocate memory at an address until it is accessed by a program. Until this access, the operating system merely keeps track that there SHOULD be real memory there. True, at one point the memory at this address WAS accessed, but we told the operating system to discard that, and replace it with protected memory instead. Memory that has never (and will never) be accessed. Thus there is no waste!