FreeBSD-EN-09:05: NULL Pointer Mapping

This is a serious issue and to begin with, credits for this advisory go to John Baldwin, Konstantin Belousov, Alan Cox, and Bjoern Zeeb. The bug affects all of the FreeBSD releases prior to that patch. The concept is that user space processes have no limitation in mapping the location of NULL pointer and this results in perfectably exploitable conditions for NULL pointer dereference vulnerabilities. For example, if a kernel process attempts to access data in NULL or an offset of it because of a NULL pointer dereference, a user could map that address and inject malicious data that could lead to code execution in the context of the kernel.
To fix this, a patch was developed which adds a new sysctl(8) option like this…

This is inserted in sys/kern/kern_exec.c and it initializes a new sysctl named “security.bsd.map_at_zero” that uses the static integer ‘map_at_zero’ which by default is set to 1. From the SYSCTL_INT() we can easily deduce that by default it is allowed to perform mappins in virtual address 0 since it is set to 1. To disable the NULL mappings users could use something like:

security.bsd.map_at_zero="0"

In their /boot/loader.conf or /etc/sysctl.conf. Function exec_new_vmspace() from kern/kern_exec.c which is responsible for destroying the old address space and allocating new stack was also changed to include a new variable:

The initial check required that VM reference count is set to 1, the minimum VM mapping would be equal to ‘sv_minuser’ and the maximum VM mapping equal to ‘sv->sv_maxuser’. As we can read from sys/sysent.h, ‘sv_minuser’ and ‘sv_maxuser’ represent:

So, the above check was simply checking the boundaries of the VM. The new check is using the newly added ‘map_at_zero’ varible, and if it contains a non-zero value, it initializes ‘sv_minuser’ with the user requested ‘sv->sv_minuser’. Otherwise, it will use the maximum allowable for that page address. Meaning, in a request similar to NULL, the result would be:

MAX(0x0, 0x1000) = 0x1000

The old check was inserted after the NULL mapping check as you can see in the above diff file. In addition to this, the else clause of that check was changed to use the appropriate min/max VM address values since the old one was using the user controlled ones directly.
This is the first protection against NULL pointer mappings in FreeBSD and I think it didn’t get the attention it should…