diff options
author | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-08 00:57:10 +0000 |
---|---|---|
committer | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-08 00:57:10 +0000 |
commit | 3c243646d76a71dcff796ae53491484ee5c11014 (patch) | |
tree | 7049b39c5b731082af18e2f33d3253df53305787 /sandbox | |
parent | d84fce15f3567df0a0e9f995412859973dbf8b3a (diff) | |
download | chromium_src-3c243646d76a71dcff796ae53491484ee5c11014.zip chromium_src-3c243646d76a71dcff796ae53491484ee5c11014.tar.gz chromium_src-3c243646d76a71dcff796ae53491484ee5c11014.tar.bz2 |
linux: make the seccomp sandbox work again
We were hitting a stack overflow on renderer startup, because of the following:
When we patch out syscalls, we need a scratch space near (within a 32-bit jump)
of the original code. We pick the scratch space as the end of the nearest empty
region available before the code we're patching. For the vdso region, the
stack lies directly before it and so the region we'd grab was directly before
the stack. This meant that as soon as the stack attempted to grow it'd fail
because it ran into our patch region, and we'd hit a stack overflow.
The fix is to specially note when we're near the stack region, and instead put
our scratch space as far away from the stack as possible.
Review URL: http://codereview.chromium.org/518071
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@35759 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r-- | sandbox/linux/seccomp/maps.cc | 66 |
1 files changed, 54 insertions, 12 deletions
diff --git a/sandbox/linux/seccomp/maps.cc b/sandbox/linux/seccomp/maps.cc index 2c64b7f..f5c37c4 100644 --- a/sandbox/linux/seccomp/maps.cc +++ b/sandbox/linux/seccomp/maps.cc @@ -150,20 +150,38 @@ std::string Maps::Iterator::name() const { return getIterator()->first; } -char* Maps::allocNearAddr(char* addr, size_t size, int prot) const { +// Test whether a line ends with "[stack]"; used for identifying the +// stack entry of /proc/self/maps. +static bool isStackLine(char* buf, char* end) { + char* ptr = buf; + for ( ; *ptr != '\n' && ptr < end; ++ptr) + ; + if (ptr < end && ptr - 7 > buf) { + return (memcmp(ptr - 7, "[stack]", 7) == 0); + } + return false; +} + +char* Maps::allocNearAddr(char* addr_target, size_t size, int prot) const { // We try to allocate memory within 1.5GB of a target address. This means, // we will be able to perform relative 32bit jumps from the target address. + const unsigned long kMaxDistance = 1536 << 20; + // In most of the code below, we just care about the numeric value of + // the address. + const long addr = reinterpret_cast<long>(addr_target); size = (size + 4095) & ~4095; Sandbox::SysCalls sys; if (sys.lseek(proc_self_maps_, 0, SEEK_SET)) { return NULL; } + // Iterate through lines of /proc/self/maps to consider each mapped + // region one at a time, looking for a gap between regions to allocate. char buf[256] = { 0 }; int len = 0, rc = 1; bool long_line = false; unsigned long gap_start = 0x10000; - char *new_addr; + void* new_addr; do { if (rc > 0) { do { @@ -177,20 +195,44 @@ char* Maps::allocNearAddr(char* addr, size_t size, int prot) const { char *ptr = buf; if (!long_line) { long_line = true; - unsigned long start = strtoul(ptr, &ptr, 16); - unsigned long stop = strtoul(ptr + 1, &ptr, 16); - if (start - gap_start >= size) { - if (reinterpret_cast<long>(addr) - static_cast<long>(start) >= 0) { - if (reinterpret_cast<long>(addr) - (start - size) < (1536 << 20)) { + // Maps lines have the form "<start address>-<end address> ... <name>". + unsigned long gap_end = strtoul(ptr, &ptr, 16); + unsigned long map_end = strtoul(ptr + 1, &ptr, 16); + + // gap_start to gap_end now covers the region of empty space before + // the current line. Now we try to see if there's a place within the + // gap we can use. + + if (gap_end - gap_start >= size) { + // Is the gap before our target address? + if (addr - static_cast<long>(gap_end) >= 0) { + if (addr - (gap_end - size) < kMaxDistance) { + unsigned long position; + if (isStackLine(ptr, buf + len)) { + // If we're adjacent to the stack, try to stay away from + // the GROWS_DOWN region. Pick the farthest away region that + // is still within the gap. + + if (addr < kMaxDistance || // Underflow protection. + addr - kMaxDistance < gap_start) { + position = gap_start; + } else { + position = addr - kMaxDistance; + } + } else { + // Otherwise, take the end of the region. + position = gap_end - size; + } new_addr = reinterpret_cast<char *>(sys.MMAP - (reinterpret_cast<void *>(start - size), size, prot, + (reinterpret_cast<void *>(position), size, prot, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0)); if (new_addr != MAP_FAILED) { goto done; } } - } else if (gap_start + size - reinterpret_cast<long>(addr) < - (1536 << 20)) { + } else if (gap_start + size - addr < kMaxDistance) { + // Gap is after the address. Above checks that we can wrap around + // through 0 to a space we'd use. new_addr = reinterpret_cast<char *>(sys.MMAP (reinterpret_cast<void *>(gap_start), size, prot, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1 ,0)); @@ -199,7 +241,7 @@ char* Maps::allocNearAddr(char* addr, size_t size, int prot) const { } } } - gap_start = stop; + gap_start = map_end; } for (;;) { if (!*ptr || *ptr++ == '\n') { @@ -213,7 +255,7 @@ char* Maps::allocNearAddr(char* addr, size_t size, int prot) const { } while (len || long_line); new_addr = NULL; done: - return new_addr; + return reinterpret_cast<char*>(new_addr); } } // namespace |