| 1 | //===-- sanitizer_win_interception.cpp -------------------- --*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // Windows-specific export surface to provide interception for parts of the |
| 10 | // runtime that are always statically linked, both for overriding user-defined |
| 11 | // functions as well as registering weak functions that the ASAN runtime should |
| 12 | // use over defaults. |
| 13 | // |
| 14 | //===----------------------------------------------------------------------===// |
| 15 | |
| 16 | #include "sanitizer_platform.h" |
| 17 | #if SANITIZER_WINDOWS |
| 18 | # include <stddef.h> |
| 19 | |
| 20 | # include "interception/interception.h" |
| 21 | # include "sanitizer_addrhashmap.h" |
| 22 | # include "sanitizer_common.h" |
| 23 | # include "sanitizer_internal_defs.h" |
| 24 | # include "sanitizer_placement_new.h" |
| 25 | # include "sanitizer_win_immortalize.h" |
| 26 | # include "sanitizer_win_interception.h" |
| 27 | |
| 28 | using namespace __sanitizer; |
| 29 | |
| 30 | extern "C" void *__ImageBase; |
| 31 | |
| 32 | namespace __sanitizer { |
| 33 | |
| 34 | static uptr GetSanitizerDllExport(const char *export_name) { |
| 35 | const uptr function_address = |
| 36 | __interception::InternalGetProcAddress(&__ImageBase, export_name); |
| 37 | if (function_address == 0) { |
| 38 | Report("ERROR: Failed to find sanitizer DLL export '%s'\n" , export_name); |
| 39 | CHECK("Failed to find sanitizer DLL export" && 0); |
| 40 | } |
| 41 | return function_address; |
| 42 | } |
| 43 | |
| 44 | struct WeakCallbackList { |
| 45 | explicit constexpr WeakCallbackList(RegisterWeakFunctionCallback cb) |
| 46 | : callback(cb), next(nullptr) {} |
| 47 | |
| 48 | static void *operator new(size_t size) { return InternalAlloc(size); } |
| 49 | |
| 50 | static void operator delete(void *p) { InternalFree(p); } |
| 51 | |
| 52 | RegisterWeakFunctionCallback callback; |
| 53 | WeakCallbackList *next; |
| 54 | }; |
| 55 | using WeakCallbackMap = AddrHashMap<WeakCallbackList *, 11>; |
| 56 | |
| 57 | static WeakCallbackMap *GetWeakCallbackMap() { |
| 58 | return &immortalize<WeakCallbackMap>(); |
| 59 | } |
| 60 | |
| 61 | void AddRegisterWeakFunctionCallback(uptr export_address, |
| 62 | RegisterWeakFunctionCallback cb) { |
| 63 | WeakCallbackMap::Handle h_find_or_create(GetWeakCallbackMap(), export_address, |
| 64 | false, true); |
| 65 | CHECK(h_find_or_create.exists()); |
| 66 | if (h_find_or_create.created()) { |
| 67 | *h_find_or_create = new WeakCallbackList(cb); |
| 68 | } else { |
| 69 | (*h_find_or_create)->next = new WeakCallbackList(cb); |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | static void RunWeakFunctionCallbacks(uptr export_address) { |
| 74 | WeakCallbackMap::Handle h_find(GetWeakCallbackMap(), export_address, false, |
| 75 | false); |
| 76 | if (!h_find.exists()) { |
| 77 | return; |
| 78 | } |
| 79 | |
| 80 | WeakCallbackList *list = *h_find; |
| 81 | do { |
| 82 | list->callback(); |
| 83 | } while ((list = list->next)); |
| 84 | } |
| 85 | |
| 86 | } // namespace __sanitizer |
| 87 | |
| 88 | extern "C" __declspec(dllexport) bool __cdecl __sanitizer_override_function( |
| 89 | const char *export_name, const uptr user_function, |
| 90 | uptr *const old_user_function) { |
| 91 | CHECK(export_name); |
| 92 | CHECK(user_function); |
| 93 | |
| 94 | const uptr sanitizer_function = GetSanitizerDllExport(export_name); |
| 95 | |
| 96 | const bool function_overridden = __interception::OverrideFunction( |
| 97 | user_function, sanitizer_function, old_user_function); |
| 98 | if (!function_overridden) { |
| 99 | Report( |
| 100 | "ERROR: Failed to override local function at '%p' with sanitizer " |
| 101 | "function '%s'\n" , |
| 102 | user_function, export_name); |
| 103 | CHECK("Failed to replace local function with sanitizer version." && 0); |
| 104 | } |
| 105 | |
| 106 | return function_overridden; |
| 107 | } |
| 108 | |
| 109 | extern "C" |
| 110 | __declspec(dllexport) bool __cdecl __sanitizer_override_function_by_addr( |
| 111 | const uptr source_function, const uptr target_function, |
| 112 | uptr *const old_target_function) { |
| 113 | CHECK(source_function); |
| 114 | CHECK(target_function); |
| 115 | |
| 116 | const bool function_overridden = __interception::OverrideFunction( |
| 117 | target_function, source_function, old_target_function); |
| 118 | if (!function_overridden) { |
| 119 | Report( |
| 120 | "ERROR: Failed to override function at '%p' with function at " |
| 121 | "'%p'\n" , |
| 122 | target_function, source_function); |
| 123 | CHECK("Failed to apply function override." && 0); |
| 124 | } |
| 125 | |
| 126 | return function_overridden; |
| 127 | } |
| 128 | |
| 129 | extern "C" |
| 130 | __declspec(dllexport) bool __cdecl __sanitizer_register_weak_function( |
| 131 | const char *export_name, const uptr user_function, |
| 132 | uptr *const old_user_function) { |
| 133 | CHECK(export_name); |
| 134 | CHECK(user_function); |
| 135 | |
| 136 | const uptr sanitizer_function = GetSanitizerDllExport(export_name); |
| 137 | |
| 138 | const bool function_overridden = __interception::OverrideFunction( |
| 139 | sanitizer_function, user_function, old_user_function); |
| 140 | if (!function_overridden) { |
| 141 | Report( |
| 142 | "ERROR: Failed to register local function at '%p' to be used in " |
| 143 | "place of sanitizer function '%s'\n." , |
| 144 | user_function, export_name); |
| 145 | CHECK("Failed to register weak function." && 0); |
| 146 | } |
| 147 | |
| 148 | // Note that thread-safety of RunWeakFunctionCallbacks in InitializeFlags |
| 149 | // depends on __sanitizer_register_weak_functions being called during the |
| 150 | // loader lock. |
| 151 | RunWeakFunctionCallbacks(sanitizer_function); |
| 152 | |
| 153 | return function_overridden; |
| 154 | } |
| 155 | |
| 156 | #endif // SANITIZER_WINDOWS |
| 157 | |