diff options
Diffstat (limited to 'driver/sanitizer_hooks_with_pc.cpp')
-rw-r--r-- | driver/sanitizer_hooks_with_pc.cpp | 187 |
1 files changed, 0 insertions, 187 deletions
diff --git a/driver/sanitizer_hooks_with_pc.cpp b/driver/sanitizer_hooks_with_pc.cpp deleted file mode 100644 index bb3ec5e1..00000000 --- a/driver/sanitizer_hooks_with_pc.cpp +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2021 Code Intelligence GmbH -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "sanitizer_hooks_with_pc.h" - -#include <cstddef> -#include <cstdint> - -// libFuzzer's compare hooks obtain the caller's address from the compiler -// builtin __builtin_return_adress. Since Java code will invoke the hooks always -// from the same native function, this builtin would always return the same -// value. Internally, the libFuzzer hooks call through to the always inlined -// HandleCmp and thus can't be mimicked without patching libFuzzer. -// -// We solve this problem via an inline assembly trampoline construction that -// translates a runtime argument `fake_pc` in the range [0, 512) into a call to -// a hook with a fake return address whose lower 9 bits are `fake_pc` up to a -// constant shift. This is achieved by pushing a return address pointing into -// 512 ret instructions at offset `fake_pc` onto the stack and then jumping -// directly to the address of the hook. -// -// Note: We only set the lowest 9 bits of the return address since only these -// bits are used by the libFuzzer value profiling mode for integer compares, see -// https://github.com/llvm/llvm-project/blob/704d92607d26e696daba596b72cb70effe79a872/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L390 -// as well as -// https://github.com/llvm/llvm-project/blob/704d92607d26e696daba596b72cb70effe79a872/compiler-rt/lib/fuzzer/FuzzerValueBitMap.h#L34 -// ValueProfileMap.AddValue() truncates its argument to 16 bits and shifts the -// PC to the left by log_2(128)=7, which means that only the lowest 16 - 7 bits -// of the return address matter. String compare hooks use the lowest 12 bits, -// but take the return address as an argument and thus don't require the -// indirection through a trampoline. - -#define REPEAT_8(a) a a a a a a a a - -#define REPEAT_512(a) REPEAT_8(REPEAT_8(REPEAT_8(a))) - -// The first four registers to pass arguments in according to the -// platform-specific x64 calling convention. -#ifdef _WIN64 -#define REG_1 "rcx" -#define REG_2 "rdx" -#define REG_3 "r8" -#define REG_4 "r9" -#else -#define REG_1 "rdi" -#define REG_2 "rsi" -#define REG_3 "rdx" -#define REG_4 "rcx" -#endif - -// Call the function at address `func` with arguments `arg1` and `arg2` while -// ensuring that the return address is `fake_pc` up to a globally constant -// offset. -__attribute__((noinline)) void trampoline(uint64_t arg1, uint64_t arg2, - void *func, uint16_t fake_pc) { - // arg1 and arg2 have to be forwarded according to the x64 calling convention. - // We also fix func and fake_pc to their registers so that we can safely use - // rax below. - [[maybe_unused]] register uint64_t arg1_loc asm(REG_1) = arg1; - [[maybe_unused]] register uint64_t arg2_loc asm(REG_2) = arg2; - [[maybe_unused]] register void *func_loc asm(REG_3) = func; - [[maybe_unused]] register uint64_t fake_pc_loc asm(REG_4) = fake_pc; - asm volatile goto( - // Load RIP-relative address of the end of this function. - "lea %l[end_of_function](%%rip), %%rax \n\t" - "push %%rax \n\t" - // Load RIP-relative address of the ret sled into rax. - "lea ret_sled(%%rip), %%rax \n\t" - // Add the offset of the fake_pc-th ret. - "add %[fake_pc], %%rax \n\t" - // Push the fake return address pointing to that ret. The hook will return - // to it and then immediately return to the end of this function. - "push %%rax \n\t" - // Call func with the fake return address on the stack. - // Function arguments arg1 and arg2 are passed unchanged in the registers - // RDI and RSI as governed by the x64 calling convention. - "jmp *%[func] \n\t" - // Append a sled of 2^9=512 ret instructions. - "ret_sled: \n\t" REPEAT_512("ret \n\t") - : - : "r"(arg1_loc), - "r"(arg2_loc), [func] "r"(func_loc), [fake_pc] "r"(fake_pc_loc) - : "memory" - : end_of_function); - -end_of_function: - return; -} - -namespace { -uintptr_t trampoline_offset = 0; -} - -void set_trampoline_offset() { - // Stores the additive inverse of the current return address modulo 0x200u in - // trampoline_offset. - trampoline_offset = - 0x200u - - (reinterpret_cast<uintptr_t>(__builtin_return_address(0)) & 0x1FFu); -} - -// Computes the additive shift that needs to be applied to the caller PC by -// caller_pc_to_fake_pc to make caller PC and resulting fake return address -// in their lowest 9 bite. This offset is constant for each binary, but may vary -// based on code generation specifics. By calibrating the trampoline, the fuzzer -// behavior is fully determined by the seed. -void CalibrateTrampoline() { - trampoline(0, 0, reinterpret_cast<void *>(&set_trampoline_offset), 0); -} - -// Masks any address down to its lower 9 bits, adjusting for the trampoline -// shift. -__attribute__((always_inline)) inline uint16_t caller_pc_to_fake_pc( - const void *caller_pc) { - return (reinterpret_cast<uintptr_t>(caller_pc) + trampoline_offset) & 0x1FFu; -} - -// The original hooks exposed by libFuzzer. All of these get the caller's -// address via __builtin_return_address(0). -extern "C" { -void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2); -void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2); -void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases); -void __sanitizer_cov_trace_div4(uint32_t val); -void __sanitizer_cov_trace_div8(uint64_t val); -void __sanitizer_cov_trace_gep(uintptr_t idx); -void __sanitizer_cov_trace_pc_indir(uintptr_t callee); -} -void __sanitizer_cov_trace_cmp4_with_pc(void *caller_pc, uint32_t arg1, - uint32_t arg2) { - void *trace_cmp4 = reinterpret_cast<void *>(&__sanitizer_cov_trace_cmp4); - auto fake_pc = caller_pc_to_fake_pc(caller_pc); - trampoline(static_cast<uint64_t>(arg1), static_cast<uint64_t>(arg2), - trace_cmp4, fake_pc); -} - -void __sanitizer_cov_trace_cmp8_with_pc(void *caller_pc, uint64_t arg1, - uint64_t arg2) { - void *trace_cmp8 = reinterpret_cast<void *>(&__sanitizer_cov_trace_cmp8); - auto fake_pc = caller_pc_to_fake_pc(caller_pc); - trampoline(static_cast<uint64_t>(arg1), static_cast<uint64_t>(arg2), - trace_cmp8, fake_pc); -} - -void __sanitizer_cov_trace_switch_with_pc(void *caller_pc, uint64_t val, - uint64_t *cases) { - void *trace_switch = reinterpret_cast<void *>(&__sanitizer_cov_trace_switch); - auto fake_pc = caller_pc_to_fake_pc(caller_pc); - trampoline(static_cast<uint64_t>(val), reinterpret_cast<uint64_t>(cases), - trace_switch, fake_pc); -} - -void __sanitizer_cov_trace_div4_with_pc(void *caller_pc, uint32_t val) { - void *trace_div4 = reinterpret_cast<void *>(&__sanitizer_cov_trace_div4); - auto fake_pc = caller_pc_to_fake_pc(caller_pc); - trampoline(static_cast<uint64_t>(val), 0, trace_div4, fake_pc); -} - -void __sanitizer_cov_trace_div8_with_pc(void *caller_pc, uint64_t val) { - void *trace_div8 = reinterpret_cast<void *>(&__sanitizer_cov_trace_div8); - auto fake_pc = caller_pc_to_fake_pc(caller_pc); - trampoline(static_cast<uint64_t>(val), 0, trace_div8, fake_pc); -} - -void __sanitizer_cov_trace_gep_with_pc(void *caller_pc, uintptr_t idx) { - void *trace_gep = reinterpret_cast<void *>(&__sanitizer_cov_trace_gep); - auto fake_pc = caller_pc_to_fake_pc(caller_pc); - trampoline(static_cast<uint64_t>(idx), 0, trace_gep, fake_pc); -} - -void __sanitizer_cov_trace_pc_indir_with_pc(void *caller_pc, uintptr_t callee) { - void *trace_pc_indir = - reinterpret_cast<void *>(&__sanitizer_cov_trace_pc_indir); - auto fake_pc = caller_pc_to_fake_pc(caller_pc); - trampoline(static_cast<uint64_t>(callee), 0, trace_pc_indir, fake_pc); -} |