Fun with NULL pointers

This post is actually stimulated by a recent kernel bug

I always took it as granted that null pointers aren't accessible. However, after reading that lwn article I learned that it's by default allowed to map a page at address 0. This behavior of course can be controlled by the vm.mmap_min_addr sysctl knob. (UPDATE: According to this blog, this knob was introduced since kernel 2.6.23.)

The following code demonstrates my point.

$ cat a.c
#include "stdio.h"
#include "sys/mman.h"

#define PAGESIZE 4096
#define STEP (4096/sizeof(int))

int main() {
  int *p = 0;
  void *ret = mmap(p, 4096, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
  if ((int)ret == -1) {
    perror("mmap");
    // Look for the lowest mappable address
    do {
      p += STEP;
      ret = mmap(p, 4096, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
    } while ((int)ret == -1);
  }
  *p = 3;
  printf("*(%p) == %d\n", p, *p);
}

By default, we can establish a mapping at address 0.

$ /sbin/sysctl vm.mmap_min_addr 
vm.mmap_min_addr = 0

$ ./a.out 
*((nil)) == 3

(UPDATE: On 8/15/09, Debian kernel 2.6.30-6 changed this default value to 4096. Presumably, this change is made to defeat this even more powerful exploit.)

We can enforce a restriction through sysctl. The lowest mappable address will be rounded to the next page boundary. The root user doesn't seem to be restricted at all.

$ sudo sysctl vm.mmap_min_addr=10000
vm.mmap_min_addr = 10000

$ ./a.out 
mmap: Permission denied
*(0x3000) == 3

$ sudo ./a.out 
*((nil)) == 3

Post new comment

The content of this field is kept private and will not be shown publicly.