@@ -187,8 +187,12 @@ static uptr GetMmapGranularity() {
187
187
return si.dwAllocationGranularity ;
188
188
}
189
189
190
+ UNUSED static uptr RoundDownTo (uptr size, uptr boundary) {
191
+ return size & ~(boundary - 1 );
192
+ }
193
+
190
194
UNUSED static uptr RoundUpTo (uptr size, uptr boundary) {
191
- return (size + boundary - 1 ) & ~( boundary - 1 );
195
+ return RoundDownTo (size + boundary - 1 , boundary);
192
196
}
193
197
194
198
// FIXME: internal_str* and internal_mem* functions should be moved from the
@@ -285,8 +289,11 @@ static void WriteJumpInstruction(uptr from, uptr target) {
285
289
286
290
static void WriteShortJumpInstruction (uptr from, uptr target) {
287
291
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);
289
295
InterceptionFailed ();
296
+ }
290
297
*(u8*)from = 0xEB ;
291
298
*(u8*)(from + 1 ) = (u8)offset;
292
299
}
@@ -340,32 +347,78 @@ struct TrampolineMemoryRegion {
340
347
uptr max_size;
341
348
};
342
349
343
- UNUSED static const uptr kTrampolineScanLimitRange = 1ull << 31 ; // 2 gig
350
+ UNUSED static const uptr kTrampolineRangeLimit = 1ull << 31 ; // 2 gig
344
351
static const int kMaxTrampolineRegion = 1024 ;
345
352
static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion ];
346
353
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
+
352
388
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);
354
394
return nullptr ;
395
+ }
355
396
356
- // Check whether a region can be allocated at |address |.
397
+ // Check whether a region can be allocated at |addr |.
357
398
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);
362
407
return page;
363
408
}
364
409
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);
368
416
}
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);
369
422
return nullptr ;
370
423
#else
371
424
return ::VirtualAlloc (nullptr ,
@@ -387,37 +440,50 @@ void TestOnlyReleaseTrampolineRegions() {
387
440
}
388
441
389
442
static 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;
391
446
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.
396
449
// This allows us to not just jump to the trampoline with a PC-relative
397
450
// offset, but to relocate any instructions that we copy to the trampoline
398
451
// which have references to the original module. If we can't find the base
399
452
// 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 .
401
454
HMODULE module;
402
455
if (::GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
403
456
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
404
457
(LPCWSTR)func_address, &module)) {
405
458
MODULEINFO module_info;
406
459
if (::GetModuleInformation (::GetCurrentProcess (), module,
407
460
&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;
409
464
}
410
465
}
411
- #endif
412
466
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.
414
479
TrampolineMemoryRegion *region = nullptr ;
415
480
for (size_t bucket = 0 ; bucket < kMaxTrampolineRegion ; ++bucket) {
416
481
TrampolineMemoryRegion* current = &TrampolineRegions[bucket];
417
482
if (current->content == 0 ) {
418
483
// No valid region found, allocate a new region.
419
484
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);
421
487
if (content == nullptr )
422
488
return 0U ;
423
489
@@ -427,13 +493,9 @@ static uptr AllocateMemoryForTrampoline(uptr func_address, size_t size) {
427
493
region = current;
428
494
break ;
429
495
} 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 ;
437
499
// The space can be allocated in the current region.
438
500
region = current;
439
501
break ;
@@ -872,8 +934,14 @@ static bool CopyInstructions(uptr to, uptr from, size_t size) {
872
934
// this will be untrue if relocated_offset \notin [-2**31, 2**31)
873
935
s64 delta = to - from;
874
936
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);
876
943
return false ;
944
+ }
877
945
# else
878
946
// on 32-bit, the relative offset will always be correct
879
947
s32 delta = to - from;
@@ -1167,19 +1235,27 @@ uptr InternalGetProcAddress(void *module, const char *func_name) {
1167
1235
// exported directory.
1168
1236
char function_name[256 ];
1169
1237
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);
1171
1240
InterceptionFailed ();
1241
+ }
1172
1242
1173
1243
_memcpy (function_name, func, funtion_name_length);
1174
1244
function_name[funtion_name_length] = ' \0 ' ;
1175
1245
char * separator = _strchr (function_name, ' .' );
1176
- if (!separator)
1246
+ if (!separator) {
1247
+ ReportError (" interception_win: no separator in '%s'\n " ,
1248
+ function_name);
1177
1249
InterceptionFailed ();
1250
+ }
1178
1251
*separator = ' \0 ' ;
1179
1252
1180
1253
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);
1182
1257
InterceptionFailed ();
1258
+ }
1183
1259
return InternalGetProcAddress (redirected_module, separator + 1 );
1184
1260
}
1185
1261
0 commit comments