@@ -187,8 +187,12 @@ static uptr GetMmapGranularity() {
187187 return si.dwAllocationGranularity ;
188188}
189189
190+ UNUSED static uptr RoundDownTo (uptr size, uptr boundary) {
191+ return size & ~(boundary - 1 );
192+ }
193+
190194UNUSED static uptr RoundUpTo (uptr size, uptr boundary) {
191- return (size + boundary - 1 ) & ~( boundary - 1 );
195+ return RoundDownTo (size + boundary - 1 , boundary);
192196}
193197
194198// FIXME: internal_str* and internal_mem* functions should be moved from the
@@ -285,8 +289,11 @@ static void WriteJumpInstruction(uptr from, uptr target) {
285289
286290static void WriteShortJumpInstruction (uptr from, uptr target) {
287291 sptr offset = target - from - kShortJumpInstructionLength ;
288- if (offset < -128 || offset > 127 )
292+ if (offset < -128 || offset > 127 ) {
293+ ReportError (" interception_win: cannot write short jmp from %p to %p\n " ,
294+ (void *)from, (void *)target);
289295 InterceptionFailed ();
296+ }
290297 *(u8 *)from = 0xEB ;
291298 *(u8 *)(from + 1 ) = (u8 )offset;
292299}
@@ -340,32 +347,78 @@ struct TrampolineMemoryRegion {
340347 uptr max_size;
341348};
342349
343- UNUSED static const uptr kTrampolineScanLimitRange = 1ull << 31 ; // 2 gig
350+ UNUSED static const uptr kTrampolineRangeLimit = 1ull << 31 ; // 2 gig
344351static const int kMaxTrampolineRegion = 1024 ;
345352static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion ];
346353
347- static void *AllocateTrampolineRegion (uptr image_address, size_t granularity) {
348- #if SANITIZER_WINDOWS64
349- uptr address = image_address;
350- uptr scanned = 0 ;
351- while (scanned < kTrampolineScanLimitRange ) {
354+ static void *AllocateTrampolineRegion (uptr min_addr, uptr max_addr,
355+ uptr func_addr, size_t granularity) {
356+ # if SANITIZER_WINDOWS64
357+ // Clamp {min,max}_addr to the accessible address space.
358+ SYSTEM_INFO system_info;
359+ ::GetSystemInfo (&system_info);
360+ uptr min_virtual_addr =
361+ RoundUpTo ((uptr)system_info.lpMinimumApplicationAddress , granularity);
362+ uptr max_virtual_addr =
363+ RoundDownTo ((uptr)system_info.lpMaximumApplicationAddress , granularity);
364+ if (min_addr < min_virtual_addr)
365+ min_addr = min_virtual_addr;
366+ if (max_addr > max_virtual_addr)
367+ max_addr = max_virtual_addr;
368+
369+ // This loop probes the virtual address space to find free memory in the
370+ // [min_addr, max_addr] interval. The search starts from func_addr and
371+ // proceeds "outwards" towards the interval bounds using two probes, lo_addr
372+ // and hi_addr, for addresses lower/higher than func_addr. At each step, it
373+ // considers the probe closest to func_addr. If that address is not free, the
374+ // probe is advanced (lower or higher depending on the probe) to the next
375+ // memory block and the search continues.
376+ uptr lo_addr = RoundDownTo (func_addr, granularity);
377+ uptr hi_addr = RoundUpTo (func_addr, granularity);
378+ while (lo_addr >= min_addr || hi_addr <= max_addr) {
379+ // Consider the in-range address closest to func_addr.
380+ uptr addr;
381+ if (lo_addr < min_addr)
382+ addr = hi_addr;
383+ else if (hi_addr > max_addr)
384+ addr = lo_addr;
385+ else
386+ addr = (hi_addr - func_addr < func_addr - lo_addr) ? hi_addr : lo_addr;
387+
352388 MEMORY_BASIC_INFORMATION info;
353- if (!::VirtualQuery ((void *)address, &info, sizeof (info)))
389+ if (!::VirtualQuery ((void *)addr, &info, sizeof (info))) {
390+ ReportError (
391+ " interception_win: VirtualQuery in AllocateTrampolineRegion failed "
392+ " for %p\n " ,
393+ (void *)addr);
354394 return nullptr ;
395+ }
355396
356- // Check whether a region can be allocated at |address |.
397+ // Check whether a region can be allocated at |addr |.
357398 if (info.State == MEM_FREE && info.RegionSize >= granularity) {
358- void *page = ::VirtualAlloc ((void *)RoundUpTo (address, granularity),
359- granularity,
360- MEM_RESERVE | MEM_COMMIT,
361- PAGE_EXECUTE_READWRITE);
399+ void *page =
400+ ::VirtualAlloc ((void *)addr, granularity, MEM_RESERVE | MEM_COMMIT,
401+ PAGE_EXECUTE_READWRITE);
402+ if (page == nullptr )
403+ ReportError (
404+ " interception_win: VirtualAlloc in AllocateTrampolineRegion failed "
405+ " for %p\n " ,
406+ (void *)addr);
362407 return page;
363408 }
364409
365- // Move to the next region.
366- address = (uptr)info.BaseAddress + info.RegionSize ;
367- scanned += info.RegionSize ;
410+ if (addr == lo_addr)
411+ lo_addr =
412+ RoundDownTo ((uptr)info.AllocationBase - granularity, granularity);
413+ if (addr == hi_addr)
414+ hi_addr =
415+ RoundUpTo ((uptr)info.BaseAddress + info.RegionSize , granularity);
368416 }
417+
418+ ReportError (
419+ " interception_win: AllocateTrampolineRegion failed to find free memory; "
420+ " min_addr: %p, max_addr: %p, func_addr: %p, granularity: %zu\n " ,
421+ (void *)min_addr, (void *)max_addr, granularity);
369422 return nullptr ;
370423#else
371424 return ::VirtualAlloc (nullptr ,
@@ -387,37 +440,50 @@ void TestOnlyReleaseTrampolineRegions() {
387440}
388441
389442static uptr AllocateMemoryForTrampoline (uptr func_address, size_t size) {
390- uptr image_address = func_address;
443+ # if SANITIZER_WINDOWS64
444+ uptr min_addr = func_address - kTrampolineRangeLimit ;
445+ uptr max_addr = func_address + kTrampolineRangeLimit - size;
391446
392- #if SANITIZER_WINDOWS64
393- // Allocate memory after the module (DLL or EXE file), but within 2GB
394- // of the start of the module so that any address within the module can be
395- // referenced with PC-relative operands.
447+ // Allocate memory within 2GB of the module (DLL or EXE file) so that any
448+ // address within the module can be referenced with PC-relative operands.
396449 // This allows us to not just jump to the trampoline with a PC-relative
397450 // offset, but to relocate any instructions that we copy to the trampoline
398451 // which have references to the original module. If we can't find the base
399452 // address of the module (e.g. if func_address is in mmap'ed memory), just
400- // use func_address as is .
453+ // stay within 2GB of func_address .
401454 HMODULE module ;
402455 if (::GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
403456 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
404457 (LPCWSTR)func_address, &module )) {
405458 MODULEINFO module_info;
406459 if (::GetModuleInformation (::GetCurrentProcess (), module ,
407460 &module_info, sizeof (module_info))) {
408- image_address = (uptr)module_info.lpBaseOfDll ;
461+ min_addr = (uptr)module_info.lpBaseOfDll + module_info.SizeOfImage -
462+ kTrampolineRangeLimit ;
463+ max_addr = (uptr)module_info.lpBaseOfDll + kTrampolineRangeLimit - size;
409464 }
410465 }
411- #endif
412466
413- // Find a region within 2G with enough space to allocate |size| bytes.
467+ // Check for overflow.
468+ if (min_addr > func_address)
469+ min_addr = 0 ;
470+ if (max_addr < func_address)
471+ max_addr = ~(uptr)0 ;
472+ # else
473+ uptr min_addr = 0 ;
474+ uptr max_addr = ~min_addr;
475+ # endif
476+
477+ // Find a region within [min_addr,max_addr] with enough space to allocate
478+ // |size| bytes.
414479 TrampolineMemoryRegion *region = nullptr ;
415480 for (size_t bucket = 0 ; bucket < kMaxTrampolineRegion ; ++bucket) {
416481 TrampolineMemoryRegion* current = &TrampolineRegions[bucket];
417482 if (current->content == 0 ) {
418483 // No valid region found, allocate a new region.
419484 size_t bucket_size = GetMmapGranularity ();
420- void *content = AllocateTrampolineRegion (image_address, bucket_size);
485+ void *content = AllocateTrampolineRegion (min_addr, max_addr, func_address,
486+ bucket_size);
421487 if (content == nullptr )
422488 return 0U ;
423489
@@ -427,13 +493,9 @@ static uptr AllocateMemoryForTrampoline(uptr func_address, size_t size) {
427493 region = current;
428494 break ;
429495 } else if (current->max_size - current->allocated_size > size) {
430- #if SANITIZER_WINDOWS64
431- // In 64-bits, the memory space must be allocated within 2G boundary.
432- uptr next_address = current->content + current->allocated_size ;
433- if (next_address < image_address ||
434- next_address - image_address >= 0x7FFF0000 )
435- continue ;
436- #endif
496+ uptr next_address = current->content + current->allocated_size ;
497+ if (next_address < min_addr || next_address > max_addr)
498+ continue ;
437499 // The space can be allocated in the current region.
438500 region = current;
439501 break ;
@@ -872,8 +934,14 @@ static bool CopyInstructions(uptr to, uptr from, size_t size) {
872934 // this will be untrue if relocated_offset \notin [-2**31, 2**31)
873935 s64 delta = to - from;
874936 s64 relocated_offset = *(s32 *)(to + cursor + rel_offset) - delta;
875- if (-0x8000'0000ll > relocated_offset || relocated_offset > 0x7FFF'FFFFll )
937+ if (-0x8000'0000ll > relocated_offset ||
938+ relocated_offset > 0x7FFF'FFFFll ) {
939+ ReportError (
940+ " interception_win: CopyInstructions relocated_offset %lld outside "
941+ " 32-bit range\n " ,
942+ (long long )relocated_offset);
876943 return false ;
944+ }
877945# else
878946 // on 32-bit, the relative offset will always be correct
879947 s32 delta = to - from;
@@ -1167,19 +1235,27 @@ uptr InternalGetProcAddress(void *module, const char *func_name) {
11671235 // exported directory.
11681236 char function_name[256 ];
11691237 size_t funtion_name_length = _strlen (func);
1170- if (funtion_name_length >= sizeof (function_name) - 1 )
1238+ if (funtion_name_length >= sizeof (function_name) - 1 ) {
1239+ ReportError (" interception_win: func too long: '%s'\n " , func);
11711240 InterceptionFailed ();
1241+ }
11721242
11731243 _memcpy (function_name, func, funtion_name_length);
11741244 function_name[funtion_name_length] = ' \0 ' ;
11751245 char * separator = _strchr (function_name, ' .' );
1176- if (!separator)
1246+ if (!separator) {
1247+ ReportError (" interception_win: no separator in '%s'\n " ,
1248+ function_name);
11771249 InterceptionFailed ();
1250+ }
11781251 *separator = ' \0 ' ;
11791252
11801253 void * redirected_module = GetModuleHandleA (function_name);
1181- if (!redirected_module)
1254+ if (!redirected_module) {
1255+ ReportError (" interception_win: GetModuleHandleA failed for '%s'\n " ,
1256+ function_name);
11821257 InterceptionFailed ();
1258+ }
11831259 return InternalGetProcAddress (redirected_module, separator + 1 );
11841260 }
11851261
0 commit comments