The malloc
function uses both sbrk
and mmap
functions. Now the sbrk
function increases or decreases the data segment. So it grows linearly. Now my question is, is this linearity always maintained, or for example, an mmap
call can allocate memory overlapping the data segment?
I'm talking about multithreaded programs running on multicore systems. This blog talks about some serious flaws of sbrk
for multithreaded programs, and it points out that it is possible that memory allocated with sbrk
can be intermingled with memory alloacted with mmap
(The sbrk
heap could become discontinuous because a mmaped
region or a shared object obstructs the growth of the heap).
That blog post doesn't see the forest for the trees; only the malloc
implementation is allowed to call sbrk
with a nonzero argument. More precisely, most malloc
implementations for Unix will stop functioning correctly (and by that I mean "your program will crash") if application code calls sbrk
with a nonzero argument. If you want to make a large allocation directly from the OS you must use mmap
to do it.
(It is true that in a multi-threaded program, malloc
must internally wrap a mutex around its calls to sbrk
, but that's an implementation detail. POSIX says malloc
is thread safe, that's the important thing for an application programmer.)
mmap
will not allocate memory overlapping the brk
area unless you use MAP_FIXED
. If you use MAP_FIXED
and your program blows up you get to keep all the pieces.
The kernel tries to avoid doing it, but mmap
in normal operation could conceivably allocate memory close to the top of the brk
area. If this happens, a subsequent sbrk
call that would collide with the mmap
region will fail. It will not allocate discontiguous memory. Good implementations of malloc
ought to detect this condition and start using mmap
for everything. I have not actually tried it, but a test program would be pretty easy to write.
is this linearity always maintained, or for example, an mmap call can allocate memory overlapping the data segment?
Observed behavior is that the brk area is always linear. Implementation details: If enlarging the brk area is not possible, for example due to a blocking mapping, glibc will switch to mmap-only. Small allocations (<128KB) seem to be obtained by glibc via brk if possible, so blocking that with:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
int main(void)
{
int i;
for (i = 0; i < 1024; ++i) {
malloc(2048);
if (i == 512) {
void *r, *end = sbrk(0);
r = mmap(end, 4096, PROT_NONE,
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
}
}
}
when straced, yields indeed
[...]
brk(0x1e7d000) = 0x1e7d000
mmap(0x1e7d000, 4096, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0) = 0x1e7d000
brk(0x1e9e000) = 0x1e7d000 <-- (!)
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbfd9bc9000
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With