jemalloc() assumes DSS is aligned

From: John Baldwin <jhb_at_freebsd.org>
Date: Wed, 13 Jun 2012 11:31:21 -0400
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)
_at__at_ -5132,6 +5132,9 _at__at_ 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)
_at__at_ -123,12 +123,16 _at__at_ 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
Received on Wed Jun 13 2012 - 13:31:22 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:40:28 UTC