Skip to content

Commit 45eb656

Browse files
thanmmemfrob
authored and
memfrob
committed
tsan: capture shadow map start/end on init and reuse in reset
Capture the computed shadow begin/end values at the point where the shadow is first created and reuse those values on reset. Introduce new windows-specific function "ZeroMmapFixedRegion" for zeroing out an address space region previously returned by one of the MmapFixed* routines; call this function (on windows) from DoResetImpl tsan_rtl.cpp instead of MmapFixedSuperNoReserve. See golang/go#53539 (comment) for context; intended to help with updating the syso for Go's windows/amd64 race detector. Differential Revision: https://reviews.llvm.org/D128909
1 parent b2fcff0 commit 45eb656

File tree

6 files changed

+76
-7
lines changed

6 files changed

+76
-7
lines changed

compiler-rt/lib/sanitizer_common/sanitizer_common.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ bool MprotectReadOnly(uptr addr, uptr size);
120120

121121
void MprotectMallocZones(void *addr, int prot);
122122

123+
#if SANITIZER_WINDOWS
124+
// Zero previously mmap'd memory. Currently used only on Windows.
125+
bool ZeroMmapFixedRegion(uptr fixed_addr, uptr size) WARN_UNUSED_RESULT;
126+
#endif
127+
123128
#if SANITIZER_LINUX
124129
// Unmap memory. Currently only used on Linux.
125130
void UnmapFromTo(uptr from, uptr to);

compiler-rt/lib/sanitizer_common/sanitizer_win.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,17 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
234234
return (void *)mapped_addr;
235235
}
236236

237+
// ZeroMmapFixedRegion zero's out a region of memory previously returned from a
238+
// call to one of the MmapFixed* helpers. On non-windows systems this would be
239+
// done with another mmap, but on windows remapping is not an option.
240+
// VirtualFree(DECOMMIT)+VirtualAlloc(RECOMMIT) would also be a way to zero the
241+
// memory, but we can't do this atomically, so instead we fall back to using
242+
// internal_memset.
243+
bool ZeroMmapFixedRegion(uptr fixed_addr, uptr size) {
244+
internal_memset((void*) fixed_addr, 0, size);
245+
return true;
246+
}
247+
237248
bool MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
238249
// FIXME: is this really "NoReserve"? On Win32 this does not matter much,
239250
// but on Win64 it does.

compiler-rt/lib/tsan/go/build.bat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ gcc ^
5757
-Wno-format ^
5858
-Wno-maybe-uninitialized ^
5959
-DSANITIZER_DEBUG=0 ^
60+
-DSANITIZER_WINDOWS=1 ^
6061
-O3 ^
6162
-fomit-frame-pointer ^
6263
-msse3 ^

compiler-rt/lib/tsan/rtl/tsan_platform.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ struct MappingGo48 {
394394
0300 0000 0000 - 0700 0000 0000: -
395395
0700 0000 0000 - 0770 0000 0000: metainfo (memory blocks and sync objects)
396396
07d0 0000 0000 - 8000 0000 0000: -
397+
PIE binaries currently not supported, but it should be theoretically possible.
397398
*/
398399

399400
struct MappingGoWindows {

compiler-rt/lib/tsan/rtl/tsan_rtl.cpp

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,24 @@ static void DoResetImpl(uptr epoch) {
197197
}
198198

199199
DPrintf("Resetting shadow...\n");
200-
if (!MmapFixedSuperNoReserve(ShadowBeg(), ShadowEnd() - ShadowBeg(),
201-
"shadow")) {
200+
auto shadow_begin = ShadowBeg();
201+
auto shadow_end = ShadowEnd();
202+
#if SANITIZER_GO
203+
CHECK_NE(0, ctx->mapped_shadow_begin);
204+
shadow_begin = ctx->mapped_shadow_begin;
205+
shadow_end = ctx->mapped_shadow_end;
206+
VPrintf(2, "shadow_begin-shadow_end: (0x%zx-0x%zx)\n",
207+
shadow_begin, shadow_end);
208+
#endif
209+
210+
#if SANITIZER_WINDOWS
211+
auto resetFailed =
212+
!ZeroMmapFixedRegion(shadow_begin, shadow_end - shadow_begin);
213+
#else
214+
auto resetFailed =
215+
!MmapFixedSuperNoReserve(shadow_begin, shadow_end-shadow_begin, "shadow");
216+
#endif
217+
if (resetFailed) {
202218
Printf("failed to reset shadow memory\n");
203219
Die();
204220
}
@@ -557,18 +573,50 @@ void UnmapShadow(ThreadState *thr, uptr addr, uptr size) {
557573
#endif
558574

559575
void MapShadow(uptr addr, uptr size) {
576+
// Ensure thead registry lock held, so as to synchronize
577+
// with DoReset, which also access the mapped_shadow_* ctxt fields.
578+
ThreadRegistryLock lock0(&ctx->thread_registry);
579+
static bool data_mapped = false;
580+
581+
#if !SANITIZER_GO
560582
// Global data is not 64K aligned, but there are no adjacent mappings,
561583
// so we can get away with unaligned mapping.
562584
// CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
563585
const uptr kPageSize = GetPageSizeCached();
564586
uptr shadow_begin = RoundDownTo((uptr)MemToShadow(addr), kPageSize);
565587
uptr shadow_end = RoundUpTo((uptr)MemToShadow(addr + size), kPageSize);
566-
if (!MmapFixedSuperNoReserve(shadow_begin, shadow_end - shadow_begin,
567-
"shadow"))
588+
if (!MmapFixedNoReserve(shadow_begin, shadow_end - shadow_begin, "shadow"))
568589
Die();
590+
#else
591+
uptr shadow_begin = RoundDownTo((uptr)MemToShadow(addr), (64 << 10));
592+
uptr shadow_end = RoundUpTo((uptr)MemToShadow(addr + size), (64 << 10));
593+
VPrintf(2, "MapShadow for (0x%zx-0x%zx), begin/end: (0x%zx-0x%zx)\n",
594+
addr, addr + size, shadow_begin, shadow_end);
595+
596+
if (!data_mapped) {
597+
// First call maps data+bss.
598+
if (!MmapFixedSuperNoReserve(shadow_begin, shadow_end - shadow_begin, "shadow"))
599+
Die();
600+
} else {
601+
VPrintf(2, "ctx->mapped_shadow_{begin,end} = (0x%zx-0x%zx)\n",
602+
ctx->mapped_shadow_begin, ctx->mapped_shadow_end);
603+
// Second and subsequent calls map heap.
604+
if (shadow_end <= ctx->mapped_shadow_end)
605+
return;
606+
if (ctx->mapped_shadow_begin < shadow_begin)
607+
ctx->mapped_shadow_begin = shadow_begin;
608+
if (shadow_begin < ctx->mapped_shadow_end)
609+
shadow_begin = ctx->mapped_shadow_end;
610+
VPrintf(2, "MapShadow begin/end = (0x%zx-0x%zx)\n",
611+
shadow_begin, shadow_end);
612+
if (!MmapFixedSuperNoReserve(shadow_begin, shadow_end - shadow_begin,
613+
"shadow"))
614+
Die();
615+
ctx->mapped_shadow_end = shadow_end;
616+
}
617+
#endif
569618

570619
// Meta shadow is 2:1, so tread carefully.
571-
static bool data_mapped = false;
572620
static uptr mapped_meta_end = 0;
573621
uptr meta_begin = (uptr)MemToMeta(addr);
574622
uptr meta_end = (uptr)MemToMeta(addr + size);
@@ -585,8 +633,7 @@ void MapShadow(uptr addr, uptr size) {
585633
// Windows wants 64K alignment.
586634
meta_begin = RoundDownTo(meta_begin, 64 << 10);
587635
meta_end = RoundUpTo(meta_end, 64 << 10);
588-
if (meta_end <= mapped_meta_end)
589-
return;
636+
CHECK_GT(meta_end, mapped_meta_end);
590637
if (meta_begin < mapped_meta_end)
591638
meta_begin = mapped_meta_end;
592639
if (!MmapFixedSuperNoReserve(meta_begin, meta_end - meta_begin,

compiler-rt/lib/tsan/rtl/tsan_rtl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,10 @@ struct Context {
372372
uptr trace_part_total_allocated SANITIZER_GUARDED_BY(slot_mtx);
373373
uptr trace_part_recycle_finished SANITIZER_GUARDED_BY(slot_mtx);
374374
uptr trace_part_finished_excess SANITIZER_GUARDED_BY(slot_mtx);
375+
#if SANITIZER_GO
376+
uptr mapped_shadow_begin;
377+
uptr mapped_shadow_end;
378+
#endif
375379
};
376380

377381
extern Context *ctx; // The one and the only global runtime context.

0 commit comments

Comments
 (0)