How OpenBSD Leverages Malloc to Find Bugs
os-blog.com
os-blog.com
Valgrind is my tool of choice to find invalid reads or writes. It catches all of them plus it gives you nice information like a stacktrace.
What OpenBSD's implementation of malloc() does get you though is improved coping with memory fragmentation. Allocating memory in a continously heap brings lots of pain - jemalloc (which is the default allocator in FreeBSD and NetBSD) solves this nicely by also only using mmap() instead of sbrk() on Linux.
Note: I should have said "resource reclamation" instead of "fragmentation". See discussion with ajross below for details.
Obviously the valgrind advice is great. But I don't follow your point about mmap vs. sbrk regarding fragmentation. Fragmentation is a property of memory addresses, not the syscall used to allocate them. A big linear heap allocated with sbrk will fragment just as badly as the same heap allocated in a single block, even if it's allowed to have "holes" in it.
Maybe you're claiming that jemalloc deliberatly leaves gaps between allocations? That can help a little in practice, though the real effect is just to change the size of the fragmented blocks.
The problem with sbrk() is that if you allocate a few megabytes of memory + a tiny chunk, even after freeing everything apart from the tiny chunk near the "program break", nothing will be given back to the OS. If you use mmap() instead though, all pages can be given back apart from the one where the tiny chunk resides in. This makes for a tremendous difference sometimes.
You're talking about a resource reclamation issue. Unmapping a page is a clear signal to the kernel that the memory is unused and can be repurposed immediately. Otherwise, it needs to find and eject a page from memory using the VM system, which is more expensive (though I'd guess not a lot more expensive except in pathologically allocation-bound systems).
I'm not sure though that the VM system can reclaim memory in the heap allocated with sbrk(). At least I've never seen that before. Or do you mean "eject" as in swap out?
Be careful! It's easy to depend on -Wall and Valgrind and still overlook subtle legal but "doh!" errors in array or pointer logic. (Humbly speaking from experience...)
it's a useful debugging tool. you're not assured that use-after-free is going to be tightly temporally coupled with the free (a lot of the time it is though) and as more time passes after free() the odds increase that the dangling virtual address is re-allocated.
it is also the enemy of performance. you should probably not run production things with the heap configured in this way.
When space is cheap: All allocations are two 4k pages The returned pointer is alligned with the end of the buffer to detect overruns. (with a 16byte allignment) The page following the alloc is always denied. The space around the allocation is filled with flag values, and these are checked on free. After free the pages are held in storage for a few thousand following allocations.
The variants of this allocator does things like only doing this for specific ranges of allocation sizes or only after a certain number of allocations.
With this a good number of overruns and use after free bugs have been found.
Mostly used this technique on windows with delphi, on linux i prefer valgrind.
3k empty, 1k data, 4k empty and read/write-protected.
The extra page is needed since you can't set the memory protection flags at a higher granularity.
They also have buffer underflow protection (either overflow or underflow at a time).
But in 32 bit systems, for applications that heavily use memory, you may run out of address space
> MALLOC_CHECK_ is designed to be tolerant against simple errors, such as double calls of free() with the same argument, or overruns of a single byte (off-by-one bugs). Not all such errors can be protected against, however, and memory leaks can result.
> If MALLOC_CHECK_ is set to 0, any detected heap corruption is silently ignored;
> if set to 1, a diagnostic message is printed on stderr;
> if set to 2, abort(3) is called immediately;
> if set to 3, a diagnostic message is printed on stderr and the program is aborted.
Or just use valgrind, if you can.
To the point, I'm using "/etc/malloc.conf -> AFGJPRX" on most of my systems, which means that virtually everything that runs there is checked. Can you easily do that on Linux? How many apps are you running via valgrind on a daily basis?
If this usage bothers you I must caution you against working in a finance related field. One or two days would expose you to countless instances of using leverage in a figurative sense.