summaryrefslogtreecommitdiff
path: root/compiler-rt/lib/interception/interception_win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'compiler-rt/lib/interception/interception_win.cpp')
-rw-r--r--compiler-rt/lib/interception/interception_win.cpp152
1 files changed, 114 insertions, 38 deletions
diff --git a/compiler-rt/lib/interception/interception_win.cpp b/compiler-rt/lib/interception/interception_win.cpp
index 077a536dd2a3..cfd92619a1e1 100644
--- a/compiler-rt/lib/interception/interception_win.cpp
+++ b/compiler-rt/lib/interception/interception_win.cpp
@@ -187,8 +187,12 @@ static uptr GetMmapGranularity() {
return si.dwAllocationGranularity;
}
+UNUSED static uptr RoundDownTo(uptr size, uptr boundary) {
+ return size & ~(boundary - 1);
+}
+
UNUSED static uptr RoundUpTo(uptr size, uptr boundary) {
- return (size + boundary - 1) & ~(boundary - 1);
+ return RoundDownTo(size + boundary - 1, boundary);
}
// FIXME: internal_str* and internal_mem* functions should be moved from the
@@ -285,8 +289,11 @@ static void WriteJumpInstruction(uptr from, uptr target) {
static void WriteShortJumpInstruction(uptr from, uptr target) {
sptr offset = target - from - kShortJumpInstructionLength;
- if (offset < -128 || offset > 127)
+ if (offset < -128 || offset > 127) {
+ ReportError("interception_win: cannot write short jmp from %p to %p\n",
+ (void *)from, (void *)target);
InterceptionFailed();
+ }
*(u8*)from = 0xEB;
*(u8*)(from + 1) = (u8)offset;
}
@@ -340,32 +347,78 @@ struct TrampolineMemoryRegion {
uptr max_size;
};
-UNUSED static const uptr kTrampolineScanLimitRange = 1ull << 31; // 2 gig
+UNUSED static const uptr kTrampolineRangeLimit = 1ull << 31; // 2 gig
static const int kMaxTrampolineRegion = 1024;
static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion];
-static void *AllocateTrampolineRegion(uptr image_address, size_t granularity) {
-#if SANITIZER_WINDOWS64
- uptr address = image_address;
- uptr scanned = 0;
- while (scanned < kTrampolineScanLimitRange) {
+static void *AllocateTrampolineRegion(uptr min_addr, uptr max_addr,
+ uptr func_addr, size_t granularity) {
+# if SANITIZER_WINDOWS64
+ // Clamp {min,max}_addr to the accessible address space.
+ SYSTEM_INFO system_info;
+ ::GetSystemInfo(&system_info);
+ uptr min_virtual_addr =
+ RoundUpTo((uptr)system_info.lpMinimumApplicationAddress, granularity);
+ uptr max_virtual_addr =
+ RoundDownTo((uptr)system_info.lpMaximumApplicationAddress, granularity);
+ if (min_addr < min_virtual_addr)
+ min_addr = min_virtual_addr;
+ if (max_addr > max_virtual_addr)
+ max_addr = max_virtual_addr;
+
+ // This loop probes the virtual address space to find free memory in the
+ // [min_addr, max_addr] interval. The search starts from func_addr and
+ // proceeds "outwards" towards the interval bounds using two probes, lo_addr
+ // and hi_addr, for addresses lower/higher than func_addr. At each step, it
+ // considers the probe closest to func_addr. If that address is not free, the
+ // probe is advanced (lower or higher depending on the probe) to the next
+ // memory block and the search continues.
+ uptr lo_addr = RoundDownTo(func_addr, granularity);
+ uptr hi_addr = RoundUpTo(func_addr, granularity);
+ while (lo_addr >= min_addr || hi_addr <= max_addr) {
+ // Consider the in-range address closest to func_addr.
+ uptr addr;
+ if (lo_addr < min_addr)
+ addr = hi_addr;
+ else if (hi_addr > max_addr)
+ addr = lo_addr;
+ else
+ addr = (hi_addr - func_addr < func_addr - lo_addr) ? hi_addr : lo_addr;
+
MEMORY_BASIC_INFORMATION info;
- if (!::VirtualQuery((void*)address, &info, sizeof(info)))
+ if (!::VirtualQuery((void *)addr, &info, sizeof(info))) {
+ ReportError(
+ "interception_win: VirtualQuery in AllocateTrampolineRegion failed "
+ "for %p\n",
+ (void *)addr);
return nullptr;
+ }
- // Check whether a region can be allocated at |address|.
+ // Check whether a region can be allocated at |addr|.
if (info.State == MEM_FREE && info.RegionSize >= granularity) {
- void *page = ::VirtualAlloc((void*)RoundUpTo(address, granularity),
- granularity,
- MEM_RESERVE | MEM_COMMIT,
- PAGE_EXECUTE_READWRITE);
+ void *page =
+ ::VirtualAlloc((void *)addr, granularity, MEM_RESERVE | MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE);
+ if (page == nullptr)
+ ReportError(
+ "interception_win: VirtualAlloc in AllocateTrampolineRegion failed "
+ "for %p\n",
+ (void *)addr);
return page;
}
- // Move to the next region.
- address = (uptr)info.BaseAddress + info.RegionSize;
- scanned += info.RegionSize;
+ if (addr == lo_addr)
+ lo_addr =
+ RoundDownTo((uptr)info.AllocationBase - granularity, granularity);
+ if (addr == hi_addr)
+ hi_addr =
+ RoundUpTo((uptr)info.BaseAddress + info.RegionSize, granularity);
}
+
+ ReportError(
+ "interception_win: AllocateTrampolineRegion failed to find free memory; "
+ "min_addr: %p, max_addr: %p, func_addr: %p, granularity: %zu\n",
+ (void *)min_addr, (void *)max_addr, granularity);
return nullptr;
#else
return ::VirtualAlloc(nullptr,
@@ -387,17 +440,17 @@ void TestOnlyReleaseTrampolineRegions() {
}
static uptr AllocateMemoryForTrampoline(uptr func_address, size_t size) {
- uptr image_address = func_address;
+# if SANITIZER_WINDOWS64
+ uptr min_addr = func_address - kTrampolineRangeLimit;
+ uptr max_addr = func_address + kTrampolineRangeLimit - size;
-#if SANITIZER_WINDOWS64
- // Allocate memory after the module (DLL or EXE file), but within 2GB
- // of the start of the module so that any address within the module can be
- // referenced with PC-relative operands.
+ // Allocate memory within 2GB of the module (DLL or EXE file) so that any
+ // address within the module can be referenced with PC-relative operands.
// This allows us to not just jump to the trampoline with a PC-relative
// offset, but to relocate any instructions that we copy to the trampoline
// which have references to the original module. If we can't find the base
// address of the module (e.g. if func_address is in mmap'ed memory), just
- // use func_address as is.
+ // stay within 2GB of func_address.
HMODULE module;
if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
@@ -405,19 +458,32 @@ static uptr AllocateMemoryForTrampoline(uptr func_address, size_t size) {
MODULEINFO module_info;
if (::GetModuleInformation(::GetCurrentProcess(), module,
&module_info, sizeof(module_info))) {
- image_address = (uptr)module_info.lpBaseOfDll;
+ min_addr = (uptr)module_info.lpBaseOfDll + module_info.SizeOfImage -
+ kTrampolineRangeLimit;
+ max_addr = (uptr)module_info.lpBaseOfDll + kTrampolineRangeLimit - size;
}
}
-#endif
- // Find a region within 2G with enough space to allocate |size| bytes.
+ // Check for overflow.
+ if (min_addr > func_address)
+ min_addr = 0;
+ if (max_addr < func_address)
+ max_addr = ~(uptr)0;
+# else
+ uptr min_addr = 0;
+ uptr max_addr = ~min_addr;
+# endif
+
+ // Find a region within [min_addr,max_addr] with enough space to allocate
+ // |size| bytes.
TrampolineMemoryRegion *region = nullptr;
for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
TrampolineMemoryRegion* current = &TrampolineRegions[bucket];
if (current->content == 0) {
// No valid region found, allocate a new region.
size_t bucket_size = GetMmapGranularity();
- void *content = AllocateTrampolineRegion(image_address, bucket_size);
+ void *content = AllocateTrampolineRegion(min_addr, max_addr, func_address,
+ bucket_size);
if (content == nullptr)
return 0U;
@@ -427,13 +493,9 @@ static uptr AllocateMemoryForTrampoline(uptr func_address, size_t size) {
region = current;
break;
} else if (current->max_size - current->allocated_size > size) {
-#if SANITIZER_WINDOWS64
- // In 64-bits, the memory space must be allocated within 2G boundary.
- uptr next_address = current->content + current->allocated_size;
- if (next_address < image_address ||
- next_address - image_address >= 0x7FFF0000)
- continue;
-#endif
+ uptr next_address = current->content + current->allocated_size;
+ if (next_address < min_addr || next_address > max_addr)
+ continue;
// The space can be allocated in the current region.
region = current;
break;
@@ -872,8 +934,14 @@ static bool CopyInstructions(uptr to, uptr from, size_t size) {
// this will be untrue if relocated_offset \notin [-2**31, 2**31)
s64 delta = to - from;
s64 relocated_offset = *(s32 *)(to + cursor + rel_offset) - delta;
- if (-0x8000'0000ll > relocated_offset || relocated_offset > 0x7FFF'FFFFll)
+ if (-0x8000'0000ll > relocated_offset ||
+ relocated_offset > 0x7FFF'FFFFll) {
+ ReportError(
+ "interception_win: CopyInstructions relocated_offset %lld outside "
+ "32-bit range\n",
+ (long long)relocated_offset);
return false;
+ }
# else
// on 32-bit, the relative offset will always be correct
s32 delta = to - from;
@@ -1167,19 +1235,27 @@ uptr InternalGetProcAddress(void *module, const char *func_name) {
// exported directory.
char function_name[256];
size_t funtion_name_length = _strlen(func);
- if (funtion_name_length >= sizeof(function_name) - 1)
+ if (funtion_name_length >= sizeof(function_name) - 1) {
+ ReportError("interception_win: func too long: '%s'\n", func);
InterceptionFailed();
+ }
_memcpy(function_name, func, funtion_name_length);
function_name[funtion_name_length] = '\0';
char* separator = _strchr(function_name, '.');
- if (!separator)
+ if (!separator) {
+ ReportError("interception_win: no separator in '%s'\n",
+ function_name);
InterceptionFailed();
+ }
*separator = '\0';
void* redirected_module = GetModuleHandleA(function_name);
- if (!redirected_module)
+ if (!redirected_module) {
+ ReportError("interception_win: GetModuleHandleA failed for '%s'\n",
+ function_name);
InterceptionFailed();
+ }
return InternalGetProcAddress(redirected_module, separator + 1);
}