jemalloc() assumes DSS is aligned
John Baldwin
jhb at freebsd.org
Wed Jun 13 15:31:22 UTC 2012
I tracked down a weird bug at work on the older jemalloc in FreeBSD 8/9 that a
co-worker tripped over. Specifically, if you build the program below and link
it with gold, the program will have an _end symbol that is on an odd address
(std::nothrow results in some single-byte symbol being added to the end of the
BSS). This causes the first arena allocated by jemalloc to use an odd
address, and the rbt_nil structures for that arena's embedded trees (like
runs_avail) to be allocated on odd addresses. This interferes with the RB
trees using the low bit to distinguish red vs black. Specifically, the
program ends up setting the right node of rbt_nil to an incorrect pointer
value (the low bit gets cleared) resulting in an eventual segfault. Looking
at phkmalloc, it always applied round_page() to the results from sbrk(). I
believe that for jemalloc only the very first allocation from the DSS needs to
check for misalignment, and the patch below does fix the segfault on FreeBSD
8. I have a stab at porting the change to jemalloc 3.0.0 in HEAD, but I'm not
sure if it is quite correct. Also, I only made the DSS align on the quantum
boundary rather than a page boundary. BTW, I filed a bug with the binutils
folks as I initially thought this was a gold bug. However, POSIX doesn't make
any guarantees about the return value of sbrk(), so I think gold is not
broken.
Test program:
#include <stdio.h>
#include <new>
void foo()
{
char *c = new(std::nothrow) char[10];
delete c;
}
int
main()
{
printf("Hello world\n");
}
Tested patch against FreeBSD 8:
Index: malloc.c
===================================================================
--- malloc.c (revision 225507)
+++ malloc.c (working copy)
@@ -5132,6 +5132,9 @@ MALLOC_OUT:
#ifdef MALLOC_DSS
malloc_mutex_init(&dss_mtx);
dss_base = sbrk(0);
+ i = (uintptr_t)dss_base & QUANTUM_MASK;
+ if (i != 0)
+ dss_base = sbrk(QUANTUM - i);
dss_prev = dss_base;
dss_max = dss_base;
extent_tree_szad_new(&dss_chunks_szad);
Untested forward port to jemalloc 3.0.0:
Index: chunk_dss.c
===================================================================
--- chunk_dss.c (revision 235919)
+++ chunk_dss.c (working copy)
@@ -123,12 +123,16 @@ chunk_in_dss(void *chunk)
bool
chunk_dss_boot(void)
{
+ uintptr_t off;
cassert(config_dss);
if (malloc_mutex_init(&dss_mtx))
return (true);
dss_base = sbrk(0);
+ off = (uintptr_t)dss_base & QUANTUM_MASK;
+ if (off != 0)
+ dss_base = sbrk(QUANTUM - off);
dss_prev = dss_base;
dss_max = dss_base;
binutils ld.gold PR: http://sourceware.org/bugzilla/show_bug.cgi?id=14149
--
John Baldwin
More information about the freebsd-current
mailing list