system call allows a process to change its virtual
memory layout by adjusting the size and location of a virtual memory area.
One of the things mremap()
can do is move one virtual memory area
(VMA) into the middle of another one. In that case, the target VMA will be
split in two so that the space in the middle can be freed and reused for
the VMA being moved. As long as the calling process knows what it is doing
(it doesn't need the pages being replaced by the moved area, for example),
all of this is fine.
An interesting thing can happen in the 2.4.24 and 2.6.2 kernels, however.
The kernel enforces a limit on the maximum number of VMAs that any one
process can have. If the kernel attempts to split a VMA in response to the
sort of mremap() call described above, it will check the process's
VMA usage against the limit. Splitting requires the addition of a new VMA,
so this check is necessary. If the limit has been reached, the internal
call which splits the VMA (do_munmap()) will return a failure
status. So far, so good.
The problem is that mremap() did not check to see if
do_munmap() succeeded or not. If the split failed,
mremap() would continue anyway. The end result is that the old
target VMA would remain, with its existing permissions, but some of its
associated page table entries would be overwritten by entries from the VMA
being moved. In other words, an attacker can exploit this bug to obtain
access to a set of pages which the kernel would not otherwise have
allowed. This vulnerability can be exploited by a local hacker to obtain
root access on any Linux system running a vulnerable kernel.
The solution is to upgrade to 2.4.25 or 2.6.3, or to apply the appropriate
distributor security update. The LWN
vulnerability entry tracks the available updates. For more information
on the vulnerability, see this advisory from
to post comments)