@@ -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 " 
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