Index: library.cc |
diff --git a/library.cc b/library.cc |
index 58459435b13315c515ef46ec86fdafc892c60b67..07a41ccd2377d790a2846c704496bbede2dae6ce 100644 |
--- a/library.cc |
+++ b/library.cc |
@@ -364,6 +364,54 @@ char* Library::getScratchSpace(const Maps* maps, char* near, int needed, |
Sandbox::die("Insufficient space to intercept system call"); |
} |
+#if defined(__x86_64__) |
+static bool isCallToVsyscallPage(char* code) { |
+ // Look for these instructions, which are a call to the x86-64 |
+ // vsyscall page, which the kernel puts at a fixed address: |
+ // |
+ // 48 c7 c0 00 XX 60 ff mov $0xffffffffff60XX00,%rax |
+ // ff d0 callq *%rax |
+ // |
+ // This will not catch all calls to the vsyscall page, but it |
+ // handles the important cases that glibc contains. The vsyscall |
+ // page is deprecated, so it is unlikely that new instruction |
+ // sequences for calling it will be introduced. |
+ return (code[0] == '\x48' && |
+ code[1] == '\xc7' && |
+ code[2] == '\xc0' && |
+ code[3] == '\x00' && |
+ (code[4] == '\x00' || code[4] == '\x04' || code[4] == '\x08') && |
+ code[5] == '\x60' && |
+ code[6] == '\xff' && |
+ code[7] == '\xff' && |
+ code[8] == '\xd0'); |
+} |
+ |
+static void patchCallToVsyscallPage(char* code) { |
+ // We replace the mov+callq with these instructions: |
+ // |
+ // b8 XX XX XX XX mov $X, %eax // where X is the syscall number |
+ // 0f 05 syscall |
+ // 90 nop |
+ // 90 nop |
+ // |
+ // The syscall instruction will later be patched by the general case. |
+ if (code[4] == '\x00') { |
+ // Use __NR_gettimeofday == 96 == 0x60. |
+ const char replacement[] = "\xb8\x60\x00\x00\x00\x0f\x05\x90\x90"; |
+ memcpy(code, replacement, sizeof(replacement) - 1); |
+ } else if (code[4] == '\x04') { |
+ // Use __NR_time == 201 == 0xc9. |
+ const char replacement[] = "\xb8\xc9\x00\x00\x00\x0f\x05\x90\x90"; |
+ memcpy(code, replacement, sizeof(replacement) - 1); |
+ } else if (code[4] == '\x08') { |
+ // Use __NR_getcpu == 309 == 0x135. |
+ const char replacement[] = "\xb8\x35\x01\x00\x00\x0f\x05\x90\x90"; |
+ memcpy(code, replacement, sizeof(replacement) - 1); |
+ } |
+} |
+#endif |
+ |
void Library::patchSystemCallsInFunction(const Maps* maps, int vsys_offset, |
char* start, char* end, |
char** extraSpace, int* extraLength) { |
@@ -392,6 +440,11 @@ void Library::patchSystemCallsInFunction(const Maps* maps, int vsys_offset, |
int codeIdx = 0; |
char* ptr = start; |
while (ptr < end) { |
+ #if defined(__x86_64__) |
+ if (isCallToVsyscallPage(ptr)) { |
+ patchCallToVsyscallPage(ptr); |
+ } |
+ #endif |
// Keep a ring-buffer of the last few instruction in order to find the |
// correct place to patch the code. |
char *mod_rm; |
@@ -1031,7 +1084,8 @@ void Library::patchSystemCallsInRange(char* start, char* stop, |
for (char *ptr = start; ptr < stop; ptr++) { |
#if defined(__x86_64__) |
if ((*ptr == '\x0F' && ptr[1] == '\x05' /* SYSCALL */) || |
- (isVDSO_ && *ptr == '\xFF')) { |
+ (isVDSO_ && *ptr == '\xFF') || |
+ isCallToVsyscallPage(ptr)) { |
#elif defined(__i386__) |
if ((*ptr == '\xCD' && ptr[1] == '\x80' /* INT $0x80 */) || |
(*ptr == '\x65' && ptr[1] == '\xFF' && |