OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #define XOPEN_SOURCE 500 | 5 #define XOPEN_SOURCE 500 |
6 | 6 |
7 #include "library.h" | 7 #include "library.h" |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <elf.h> | 10 #include <elf.h> |
(...skipping 346 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
357 *extraSpace = maps->allocNearAddr(near, *extraLength, | 357 *extraSpace = maps->allocNearAddr(near, *extraLength, |
358 PROT_READ|PROT_WRITE|PROT_EXEC); | 358 PROT_READ|PROT_WRITE|PROT_EXEC); |
359 } | 359 } |
360 if (*extraSpace) { | 360 if (*extraSpace) { |
361 *extraLength -= needed; | 361 *extraLength -= needed; |
362 return *extraSpace + *extraLength; | 362 return *extraSpace + *extraLength; |
363 } | 363 } |
364 Sandbox::die("Insufficient space to intercept system call"); | 364 Sandbox::die("Insufficient space to intercept system call"); |
365 } | 365 } |
366 | 366 |
| 367 #if defined(__x86_64__) |
| 368 static bool isCallToVsyscallPage(char* code) { |
| 369 // Look for these instructions, which are a call to the x86-64 |
| 370 // vsyscall page, which the kernel puts at a fixed address: |
| 371 // |
| 372 // 48 c7 c0 00 XX 60 ff mov $0xffffffffff60XX00,%rax |
| 373 // ff d0 callq *%rax |
| 374 // |
| 375 // This will not catch all calls to the vsyscall page, but it |
| 376 // handles the important cases that glibc contains. The vsyscall |
| 377 // page is deprecated, so it is unlikely that new instruction |
| 378 // sequences for calling it will be introduced. |
| 379 return (code[0] == '\x48' && |
| 380 code[1] == '\xc7' && |
| 381 code[2] == '\xc0' && |
| 382 code[3] == '\x00' && |
| 383 (code[4] == '\x00' || code[4] == '\x04' || code[4] == '\x08') && |
| 384 code[5] == '\x60' && |
| 385 code[6] == '\xff' && |
| 386 code[7] == '\xff' && |
| 387 code[8] == '\xd0'); |
| 388 } |
| 389 |
| 390 static void patchCallToVsyscallPage(char* code) { |
| 391 // We replace the mov+callq with these instructions: |
| 392 // |
| 393 // b8 XX XX XX XX mov $X, %eax // where X is the syscall number |
| 394 // 0f 05 syscall |
| 395 // 90 nop |
| 396 // 90 nop |
| 397 // |
| 398 // The syscall instruction will later be patched by the general case. |
| 399 if (code[4] == '\x00') { |
| 400 // Use __NR_gettimeofday == 96 == 0x60. |
| 401 const char replacement[] = "\xb8\x60\x00\x00\x00\x0f\x05\x90\x90"; |
| 402 memcpy(code, replacement, sizeof(replacement) - 1); |
| 403 } else if (code[4] == '\x04') { |
| 404 // Use __NR_time == 201 == 0xc9. |
| 405 const char replacement[] = "\xb8\xc9\x00\x00\x00\x0f\x05\x90\x90"; |
| 406 memcpy(code, replacement, sizeof(replacement) - 1); |
| 407 } else if (code[4] == '\x08') { |
| 408 // Use __NR_getcpu == 309 == 0x135. |
| 409 const char replacement[] = "\xb8\x35\x01\x00\x00\x0f\x05\x90\x90"; |
| 410 memcpy(code, replacement, sizeof(replacement) - 1); |
| 411 } |
| 412 } |
| 413 #endif |
| 414 |
367 void Library::patchSystemCallsInFunction(const Maps* maps, int vsys_offset, | 415 void Library::patchSystemCallsInFunction(const Maps* maps, int vsys_offset, |
368 char* start, char* end, | 416 char* start, char* end, |
369 char** extraSpace, int* extraLength) { | 417 char** extraSpace, int* extraLength) { |
370 typedef std::set<char *, std::less<char *>, SystemAllocator<char *> > | 418 typedef std::set<char *, std::less<char *>, SystemAllocator<char *> > |
371 BranchTargets; | 419 BranchTargets; |
372 BranchTargets branch_targets; | 420 BranchTargets branch_targets; |
373 for (char *ptr = start; ptr < end; ) { | 421 for (char *ptr = start; ptr < end; ) { |
374 unsigned short insn = next_inst((const char **)&ptr, __WORDSIZE == 64); | 422 unsigned short insn = next_inst((const char **)&ptr, __WORDSIZE == 64); |
375 char *target; | 423 char *target; |
376 if ((insn >= 0x70 && insn <= 0x7F) /* Jcc */ || insn == 0xEB /* JMP */) { | 424 if ((insn >= 0x70 && insn <= 0x7F) /* Jcc */ || insn == 0xEB /* JMP */) { |
377 target = ptr + (reinterpret_cast<signed char *>(ptr))[-1]; | 425 target = ptr + (reinterpret_cast<signed char *>(ptr))[-1]; |
378 } else if (insn == 0xE8 /* CALL */ || insn == 0xE9 /* JMP */ || | 426 } else if (insn == 0xE8 /* CALL */ || insn == 0xE9 /* JMP */ || |
379 (insn >= 0x0F80 && insn <= 0x0F8F) /* Jcc */) { | 427 (insn >= 0x0F80 && insn <= 0x0F8F) /* Jcc */) { |
380 target = ptr + (reinterpret_cast<int *>(ptr))[-1]; | 428 target = ptr + (reinterpret_cast<int *>(ptr))[-1]; |
381 } else { | 429 } else { |
382 continue; | 430 continue; |
383 } | 431 } |
384 branch_targets.insert(target); | 432 branch_targets.insert(target); |
385 } | 433 } |
386 struct Code { | 434 struct Code { |
387 char* addr; | 435 char* addr; |
388 int len; | 436 int len; |
389 unsigned short insn; | 437 unsigned short insn; |
390 bool is_ip_relative; | 438 bool is_ip_relative; |
391 } code[5] = { { 0 } }; | 439 } code[5] = { { 0 } }; |
392 int codeIdx = 0; | 440 int codeIdx = 0; |
393 char* ptr = start; | 441 char* ptr = start; |
394 while (ptr < end) { | 442 while (ptr < end) { |
| 443 #if defined(__x86_64__) |
| 444 if (isCallToVsyscallPage(ptr)) { |
| 445 patchCallToVsyscallPage(ptr); |
| 446 } |
| 447 #endif |
395 // Keep a ring-buffer of the last few instruction in order to find the | 448 // Keep a ring-buffer of the last few instruction in order to find the |
396 // correct place to patch the code. | 449 // correct place to patch the code. |
397 char *mod_rm; | 450 char *mod_rm; |
398 code[codeIdx].addr = ptr; | 451 code[codeIdx].addr = ptr; |
399 code[codeIdx].insn = next_inst((const char **)&ptr, __WORDSIZE == 64, | 452 code[codeIdx].insn = next_inst((const char **)&ptr, __WORDSIZE == 64, |
400 0, 0, &mod_rm, 0, 0); | 453 0, 0, &mod_rm, 0, 0); |
401 code[codeIdx].len = ptr - code[codeIdx].addr; | 454 code[codeIdx].len = ptr - code[codeIdx].addr; |
402 code[codeIdx].is_ip_relative = | 455 code[codeIdx].is_ip_relative = |
403 #if defined(__x86_64__) | 456 #if defined(__x86_64__) |
404 mod_rm && (*mod_rm & 0xC7) == 0x5; | 457 mod_rm && (*mod_rm & 0xC7) == 0x5; |
(...skipping 619 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1024 } | 1077 } |
1025 | 1078 |
1026 void Library::patchSystemCallsInRange(char* start, char* stop, | 1079 void Library::patchSystemCallsInRange(char* start, char* stop, |
1027 char** extraSpace, int* extraLength) { | 1080 char** extraSpace, int* extraLength) { |
1028 char* func = start; | 1081 char* func = start; |
1029 int nopcount = 0; | 1082 int nopcount = 0; |
1030 bool has_syscall = false; | 1083 bool has_syscall = false; |
1031 for (char *ptr = start; ptr < stop; ptr++) { | 1084 for (char *ptr = start; ptr < stop; ptr++) { |
1032 #if defined(__x86_64__) | 1085 #if defined(__x86_64__) |
1033 if ((*ptr == '\x0F' && ptr[1] == '\x05' /* SYSCALL */) || | 1086 if ((*ptr == '\x0F' && ptr[1] == '\x05' /* SYSCALL */) || |
1034 (isVDSO_ && *ptr == '\xFF')) { | 1087 (isVDSO_ && *ptr == '\xFF') || |
| 1088 isCallToVsyscallPage(ptr)) { |
1035 #elif defined(__i386__) | 1089 #elif defined(__i386__) |
1036 if ((*ptr == '\xCD' && ptr[1] == '\x80' /* INT $0x80 */) || | 1090 if ((*ptr == '\xCD' && ptr[1] == '\x80' /* INT $0x80 */) || |
1037 (*ptr == '\x65' && ptr[1] == '\xFF' && | 1091 (*ptr == '\x65' && ptr[1] == '\xFF' && |
1038 ptr[2] == '\x15' /* CALL %gs:.. */)) { | 1092 ptr[2] == '\x15' /* CALL %gs:.. */)) { |
1039 #else | 1093 #else |
1040 #error Unsupported target platform | 1094 #error Unsupported target platform |
1041 #endif | 1095 #endif |
1042 ptr++; | 1096 ptr++; |
1043 has_syscall = true; | 1097 has_syscall = true; |
1044 nopcount = 0; | 1098 nopcount = 0; |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1187 } | 1241 } |
1188 iter = symbols_.find("__kernel_rt_sigreturn"); | 1242 iter = symbols_.find("__kernel_rt_sigreturn"); |
1189 if (iter != symbols_.end() && iter->second.st_value) { | 1243 if (iter != symbols_.end() && iter->second.st_value) { |
1190 __kernel_rt_sigreturn = asr_offset_ + iter->second.st_value; | 1244 __kernel_rt_sigreturn = asr_offset_ + iter->second.st_value; |
1191 } | 1245 } |
1192 | 1246 |
1193 return true; | 1247 return true; |
1194 } | 1248 } |
1195 | 1249 |
1196 } // namespace | 1250 } // namespace |
OLD | NEW |