2010-12-05 06:50:44

by Eric Smith

[permalink] [raw]
Subject: mmap to address zero with MAP_FIXED returns ENOPERM for non-root users?

I'm doing some work with binary translation of an executable from a
non-x86 microcontroller to run in an x86 process, and need to have part
of the memory map match that of the microcontroller. This includes
having some memory at virtual address zero. I know why this isn't
usually a good idea, and why mmap() won't give that out without MAP_FIXED.

However, when I try an anonymous mmap() to virtual address zero with
MAP_FIXED, I get ENOPERM unless running as superuser.

Is this deliberate? I really don't want to have to run my translated
executable as superuser.

strace shows the system call, so I don't think glibc is the culprit.

I'm running Fedora 14 with kernel 2.6.35.6-48.fc14.x86_64. I'm
compiling with gcc 4.5.1 with the -m32 option to get a 32-bit
executable, which I'm then running on the 64-bit kernel. However, I
seem to get the same behavior if I build a 64-bit executable. My
trivial test program is below.

Thanks!
Eric




#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>

int main (int argc, char *argv [])
{
void *p;
int fd = -1;
int offset = 0;

uint32_t addr;
uint32_t length;

if (argc != 3)
{
fprintf (stderr, "usage: %s <addr> <length>\n", argv [0]);
return 1;
}

addr = strtoul (argv [1], NULL, 0);
length = strtoul (argv [2], NULL, 0);

printf ("attempting to create anonymous mapping at addr 0x%08x,
length 0x%08x\n", addr, length);

p = mmap ((void *) addr,
length,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
fd,
offset);

if (p == MAP_FAILED)
{
perror ("mapping failed");
return 2;
}

printf ("mapped at address 0x%08x\n", (uint32_t) p);
return 0;
}


2010-12-05 07:24:53

by Kyle Moffett

[permalink] [raw]
Subject: Re: mmap to address zero with MAP_FIXED returns ENOPERM for non-root users?

On Sun, Dec 5, 2010 at 01:40, Eric Smith <[email protected]> wrote:
> I'm doing some work with binary translation of an executable from a non-x86
> microcontroller to run in an x86 process, and need to have part of the
> memory map match that of the microcontroller.  This includes having some
> memory at virtual address zero.  I know why this isn't usually a good idea,
> and why mmap() won't give that out without MAP_FIXED.

> However, when I try an anonymous mmap() to virtual address zero with
> MAP_FIXED, I get ENOPERM unless running as superuser.

Hi Eric!

This is a specific security feature designed to reduce the security
impact of a kernel NULL-pointer dereference. Specifically, the most
obvious vulnerability occurs if kernel code accidentally calls a NULL
function pointer or calls a function pointer through a NULL structure
pointer, but there are other ways to exploit it. The actual exploit
is to map a page with code at address zero and then trigger the kernel
NULL-pointer-dereference, resulting in privileged execution of your
unprivileged code.

There is a sysctl tunable "vm.mmap_min_addr" that you can change to
modify this behavior, and SELinux-enabled systems can loosen this
behavior a bit, but it's not normally something you want to give to
unprivileged processes.

Since you're performing binary translation of a microcontroller, it
may be better to perform some kind of minimal memory-map translation
as a part of that. For example, you should be able to introduce an
addition into each memory dereference operation without too much
overhead (IE: treat all microcontroller addresses as relative to a
particular "memory base address").

The Debian wiki has a pretty decent page describing the security
feature in more detail:
http://wiki.debian.org/mmap_min_addr

Cheers,
Kyle Moffett

2010-12-05 07:47:21

by Eric Smith

[permalink] [raw]
Subject: Re: mmap to address zero with MAP_FIXED returns ENOPERM for non-root users?

Kyle Moffett wrote:
> This is a specific security feature designed to reduce the security
> impact of a kernel NULL-pointer dereference.

Thanks for the explanation!

> Since you're performing binary translation of a microcontroller, it
> may be better to perform some kind of minimal memory-map translation
> as a part of that.

That's exactly what I'd hoped to avoid, as it does result in a
non-trivial performance hit. I suppose I can make it a configuration
option of my translator.

I'm curious, though. How likely are exploits where I can trick the
kernel into calling a function at 0 in my virtual address space, but not
trick it into calling a function at some non-zero address of my choosing?

Best regards,
Eric

2010-12-05 08:08:58

by Samuel Thibault

[permalink] [raw]
Subject: Re: mmap to address zero with MAP_FIXED returns ENOPERM for non-root users?

Eric Smith, le Sat 04 Dec 2010 23:47:18 -0800, a ?crit :
> I'm curious, though. How likely are exploits where I can trick the
> kernel into calling a function at 0 in my virtual address space, but not
> trick it into calling a function at some non-zero address of my choosing?

NULL pointers are everywhere in function pointers structures & such.

About translation, maybe you could use an LDT entry to make the
processor do the translation? (additional 1-byte fs: prefix).

Samuel