jemalloc/jemalloc — pull request #181 by thestinger (2014-12-31 00:49:37+00:00)
Title: prevent allocations larger than PTRDIFF_MAX

In practice, this only has an impact on 32-bit processes in environments
where it's possible to allocate over half of the address space. It wipes
out a few subtle classes of undefined behavior from pointer arithmetic
without a performance hit.

The musl C library has adopted this solution to the problem already. The
alternative is pervasively checking the size against PTRDIFF_MAX in the
code asking the allocator for buffers. It seems like the burden should
be on the allocator instead because it can be solved in one place.

For example, the following C program compiled as i386 will succeed on
x86_64, but is not actually well-defined:

#include
#include

const size_t size = 3000000000U;

int main(void) {
char *ptr = malloc(size);
if (!ptr) return 1;
char *end = ptr + size;
printf("%p\n", ptr);
printf("%p\n", end);
printf("%p\n", end - ptr);
return 0;
}

The C standard is not entirely clear if the `ptr + size` operation here
is undefined. It is clear that `end - ptr` is undefined because it
causes a ptrdiff_t overflow which is explicily UB.

LLVM interprets the standard as defining all pointer arithmetic in terms
of ptrdiff_t so both operations cause undefined overflows. The LLVM IR
(clang -S -emit-llvm) is clearly undefined, and the understanding of the
LLVM developers is that this is correct per the standard:

%8 = getelementptr inbounds i8* %7, i32 -1294967296

http://llvm.org/releases/3.5.0/docs/LangRef.html#id181

Since jemalloc moved to `(uintptr_t)ptr + size`, it does not appear to
hit this issue itself. It would hit it with `(char *)ptr + size` though.