5

I expect that due to Address Space Layout Randomization (ALSR) a process forked from another process will have different addresses returned when calling mmap. But as I found out, that was not the case. I made the following test program for that purpose. All the addresses returned by malloc are exactly the same for the parent and the child. Note that the malloc for cl1, cl2, pl1, pl2 internally uses mmap because they are large blocks.

So, my question is, why mmap is not returning different addresses even in the presence of ALSR. Maybe its because the seed for randomization here is the same for the original and forked process. Or is there any other reason?

int main()
{
  pid = fork();

  if (pid == 0)                // child
  {
    void * c1 = malloc( 4096 );
    void * c2 = malloc( 4096 );

    void * cl1 = malloc( (long)512e3 ); // internally uses mmap
    void * cl2 = malloc( (long)512e3 ); // internally uses mmap

    printf( "c1 = %p, c2 = %p, cl1 = %p, cl2 = %p!\n", c1, c2, cl1, cl2 );
  }
  else
  {
    void * p1 = malloc( 4096 );
    void * p2 = malloc( 4096 );

    void * pl1 = malloc( (long)512e3 ); // internally uses mmap
    void * pl2 = malloc( (long)512e3 ); // internally uses mmap

    printf( "p1 = %p, p2 = %p, pl1 = %p, pl2 = %p!\n", p1, p2, pl1, pl2 );
  }

  return 0;
}
MetallicPriest
  • 29,191
  • 52
  • 200
  • 356
  • I'm not sure that ASLR requires that `mmap` return different addresses; it just means that it *may* return different ones. And perhaps (just a guess!) it is triggered more by `execve` than by `fork`. For sure, if I start twice in succession your program, I'm getting different addresses. And this might change with future kernels, or perhaps with SELinux enabled ones... – Basile Starynkevitch Feb 28 '12 at 16:10
  • 2
    you might find this useful: http://xorl.wordpress.com/2011/01/16/linux-kernel-aslr-implementation/ – Necrolis Feb 28 '12 at 16:14
  • @Basile: Sure, you will get different addresses for each run, but for one run, do the addresses of the two processes (parent and child) ever differ? – MetallicPriest Feb 28 '12 at 16:15
  • I only tried 3 times, but the addresses are same in parent and child. – Basile Starynkevitch Feb 28 '12 at 17:18

2 Answers2

6

ASLR mainly randomizes the distance from the top of user-space address space down to the stack, and the distance from the bottom of stack-reserved space to the first mmap (which is probably the mapping of the dynamic linker). Any further randomization would have serious fragmenting effects on the virtual memory space, and thus would break programs that need to make large mmaps (e.g. a 1-2 GB mapping on a 32-bit machine).

I have seen some Linux distros ship patched kernels that perform much more randomization on the addresses returned by mmap. Some of them even give you mappings overlapping with the space reserved for the stack to expand into, and then when the stack grows it clobbers your mapping (resulting a huge gaping security hole, much bigger than anything non-random address assignments could have caused). Stay away from these hacks.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • So we can assume that mmap in a forked process will not always return the same address as in the parent? right? – MetallicPriest Feb 28 '12 at 16:21
  • It is (usually) true today, but I won't assume that for future kernels. Nothing specifies that, it is just the current implementation. And kernel people might want to improve it. – Basile Starynkevitch Feb 28 '12 at 17:19
  • I would avoid making assumptions one way or the other. For example it would probably be safe/reasonable to add a small amount of random padding (unmapped pages) before very large mappings (since the percentage-wise overhead/fragmentation would necessarily be very small). – R.. GitHub STOP HELPING ICE Feb 28 '12 at 17:52
4

You can't re-randomize the child's address space - all pointers would have to be touched up, and that's technically impossible (the runtime environment doesn't even know what part of your data is pointers).

So the result you are seeing is expected, the child from the fork has an exact copy of its parent address space at the time of forking, including its virtual address layout.

You'll need an exec* call to get a new address-space layout.

$ cat t.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    printf("%p\n", malloc((long)512e3));
    if ((argc > 1) && fork()) {
        execl("./a.out", "./a.out", NULL);
    }
    return 0;
}
$ gcc -Wall t.c
$ ./a.out 1
0x7f5bf6962010
0x7f3483044010
$ ./a.out 1
0x7f1ce7462010
0x7feb2adc2010

(And make sure /proc/sys/kernel/randomize_va_space is not zero too.)

Mat
  • 202,337
  • 40
  • 393
  • 406
  • OP does not seem to want mappings created before the fork to change addresses, just for new mappings after the fork to get independently random addresses. This is not technically impossible, but as described in my answer, it's a bad idea. – R.. GitHub STOP HELPING ICE Feb 28 '12 at 16:19
  • So, you mean every future mmap, sbrk, malloc etc. will return the same addresses for the parent and child processes? – MetallicPriest Feb 28 '12 at 16:19
  • 1
    @MetallicPriest: if you do them in the exact same order, for the same quantities, and no external factors come into play (resource limits, plain OOM, etc.), probably yes with a plain Linux kernel. Some patches may exist to randomize dynamic allocations, but not the base kernel. See R..'s answer for more on this. – Mat Feb 28 '12 at 16:23