Add test for patching a system call instruction
This tests patching a specific, fixed instruction sequence, whereas
the existing tests just test whatever is in the version of glibc on
the system.
Change library.cc to make the patching code easier to test: Split out
a patchSystemCallsInRange() method, since the existing methods assume
they are operating on a whole dynamically-loaded ELF object. Change
maps_ to be per-instance so that creating Library objects with
different Maps objects works.
This doesn't try to cover the various corner cases in library.cc yet.
BUG=http://code.google.com/p/seccompsandbox/issues/detail?id=17
TEST=test_patching_syscall
Review URL: http://codereview.chromium.org/8596009
git-svn-id: http://seccompsandbox.googlecode.com/svn/trunk@177 55e79e8e-603c-11de-8c10-5fe6993ea61f
diff --git a/library.cc b/library.cc
index 0842a47..5845943 100644
--- a/library.cc
+++ b/library.cc
@@ -59,7 +59,6 @@
namespace playground {
-Maps* Library::maps_;
char* Library::__kernel_vsyscall;
char* Library::__kernel_sigreturn;
char* Library::__kernel_rt_sigreturn;
@@ -70,7 +69,8 @@
asr_offset_(0),
vsys_offset_(0),
image_(0),
- image_size_(0) {
+ image_size_(0),
+ maps_(NULL) {
}
Library::~Library() {
@@ -1014,6 +1014,17 @@
const Elf_Shdr& shdr = iter->second.second;
char* start = reinterpret_cast<char *>(shdr.sh_addr + asr_offset_);
char* stop = start + shdr.sh_size;
+ patchSystemCallsInRange(start, stop, &extraSpace, &extraLength);
+
+ // Mark our scratch space as write-protected and executable.
+ if (extraSpace) {
+ Sandbox::SysCalls sys;
+ sys.mprotect(extraSpace, 4096, PROT_READ|PROT_EXEC);
+ }
+}
+
+void Library::patchSystemCallsInRange(char* start, char* stop,
+ char** extraSpace, int* extraLength) {
char* func = start;
int nopcount = 0;
bool has_syscall = false;
@@ -1047,7 +1058,7 @@
// Our quick scan of the function found a potential system call.
// Do a more thorough scan, now.
patchSystemCallsInFunction(maps_, isVDSO_ ? vsys_offset_ : 0, func,
- ptr, &extraSpace, &extraLength);
+ ptr, extraSpace, extraLength);
}
func = ptr;
}
@@ -1060,13 +1071,7 @@
// Patch any remaining system calls that were in the last function before
// the loop terminated.
patchSystemCallsInFunction(maps_, isVDSO_ ? vsys_offset_ : 0, func, stop,
- &extraSpace, &extraLength);
- }
-
- // Mark our scratch space as write-protected and executable.
- if (extraSpace) {
- Sandbox::SysCalls sys;
- sys.mprotect(extraSpace, 4096, PROT_READ|PROT_EXEC);
+ extraSpace, extraLength);
}
}
diff --git a/library.h b/library.h
index c90a9e6..5f0d781 100644
--- a/library.h
+++ b/library.h
@@ -119,6 +119,8 @@
const Elf_Shdr* getSection(const string& section);
void makeWritable(bool state) const;
void patchSystemCalls();
+ void patchSystemCallsInRange(char* start, char* stop,
+ char** extraSpace, int* extraLength);
bool isVDSO() const { return isVDSO_; }
protected:
@@ -174,7 +176,7 @@
SymbolTable symbols_;
char* image_;
size_t image_size_;
- static Maps* maps_;
+ Maps* maps_;
static char* __kernel_vsyscall;
static char* __kernel_sigreturn;
static char* __kernel_rt_sigreturn;
diff --git a/makefile b/makefile
index 9ce2759..041e875 100644
--- a/makefile
+++ b/makefile
@@ -13,6 +13,8 @@
TEST_MODS := \
tests/clone_test_helper \
tests/test_runner \
+ tests/test_patching \
+ tests/test_patching_input \
tests/test_syscalls
OBJS64 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o64/')
OBJS32 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o32/')
diff --git a/seccomp.gyp b/seccomp.gyp
index 72c4ade..baabeea 100644
--- a/seccomp.gyp
+++ b/seccomp.gyp
@@ -65,6 +65,8 @@
'reference_trusted_thread.cc',
'tests/clone_test_helper.S',
'tests/test_runner.cc',
+ 'tests/test_patching.cc',
+ 'tests/test_patching_input.S',
'tests/test_syscalls.cc',
],
'include_dirs': [
diff --git a/tests/test_patching.cc b/tests/test_patching.cc
new file mode 100644
index 0000000..bb1babb
--- /dev/null
+++ b/tests/test_patching.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+
+#include "library.h"
+#include "sandbox.h"
+#include "test_runner.h"
+
+
+extern "C" int my_getpid(void);
+extern char my_getpid_end[];
+
+void patch_range(char *start, char *end) {
+ int maps_fd;
+ CHECK_SUCCEEDS((maps_fd = open("/proc/self/maps", O_RDONLY, 0)) >= 0);
+ playground::Maps maps(maps_fd);
+ playground::Library library;
+ library.setLibraryInfo(&maps);
+ char *extra_space = NULL;
+ int extra_size = 0;
+ char *page_start = (char *) ((uintptr_t) start & ~(getpagesize() - 1));
+ CHECK_SUCCEEDS(mprotect(page_start, end - page_start,
+ PROT_READ | PROT_WRITE | PROT_EXEC) == 0);
+ library.patchSystemCallsInRange(start, end, &extra_space, &extra_size);
+ CHECK_SUCCEEDS(close(maps_fd) == 0);
+}
+
+TEST(test_patching_syscall) {
+ int pid = getpid();
+ CHECK(my_getpid() == pid);
+ char *func = (char *) my_getpid;
+ char *func_end = my_getpid_end;
+ patch_range(func, func_end);
+#if defined(__x86_64__)
+ CHECK(func[0] == '\xe9'); // e9 XX XX XX XX jmp X
+ CHECK(func[5] == '\x90'); // 90 nop
+ CHECK(func[6] == '\x90'); // 90 nop
+ CHECK(func[7] == '\xc3'); // c3 ret (unmodified)
+#elif defined(__i386__)
+ CHECK(func[0] == '\x68'); // 68 XX XX XX XX push $X
+ CHECK(func[5] == '\xc3'); // c3 ret
+ CHECK(func[6] == '\x90'); // 90 nop
+ CHECK(func[7] == '\xc3'); // c3 ret (unmodified)
+#else
+# error Unsupported target platform
+#endif
+ StartSeccompSandbox();
+ CHECK(my_getpid() == pid);
+}
diff --git a/tests/test_patching_input.S b/tests/test_patching_input.S
new file mode 100644
index 0000000..3e7126d
--- /dev/null
+++ b/tests/test_patching_input.S
@@ -0,0 +1,26 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <asm/unistd.h>
+
+
+ // This performs a system call directly so that we can test
+ // patching this instruction sequence.
+
+ .global my_getpid
+ .global my_getpid_end
+my_getpid:
+ mov $__NR_getpid, %eax
+#if defined(__x86_64__)
+ syscall
+#elif defined(__i386__)
+ int $0x80
+#else
+# error Unsupported target platform
+#endif
+ ret
+my_getpid_end:
+
+ // Tell Linux not to disable no-execute protection for the process.
+ .section .note.GNU-stack,"",@progbits
diff --git a/tests/test_runner.h b/tests/test_runner.h
index 8269027..209c00a 100644
--- a/tests/test_runner.h
+++ b/tests/test_runner.h
@@ -5,6 +5,9 @@
#ifndef TEST_RUNNER_H__
#define TEST_RUNNER_H__
+#include <errno.h>
+#include <stdio.h>
+
void intend_exit_status(int val, bool is_signal);
void add_test_case(const char *test_name, void (*test_func)());