summaryrefslogtreecommitdiffstats
path: root/sandbox/linux/seccomp/maps.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/linux/seccomp/maps.cc')
-rw-r--r--sandbox/linux/seccomp/maps.cc66
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