From 682d635aa508860c306deae23766ca5e2575583a Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Wed, 11 Jan 2023 00:42:02 +0000 Subject: [PATCH] tsan: add kBrokenAliasedMetas condition and test This fills in a gap in the tsan_shadow_test coverage: it is possible that the meta regions are aliased (e.g., the heap meta region overlaps the high app meta region). Indeed, the Aarch64_39 mapping has been silently broken in this way for quite some time. This CL checks whether the individual meta regions (for low/mid/high/heap) overlap. Note that (!kBrokenAliasedMetas && !kBrokenLinearity) implies that MemToMeta is invertible; we cannot directly test MetaToMem because that function does not exist. Differential Revision: https://reviews.llvm.org/D141445 --- compiler-rt/lib/tsan/rtl/tsan_platform.h | 16 ++++- .../lib/tsan/tests/unit/tsan_shadow_test.cpp | 69 ++++++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform.h b/compiler-rt/lib/tsan/rtl/tsan_platform.h index 2d9cc88..6527a88 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_platform.h +++ b/compiler-rt/lib/tsan/rtl/tsan_platform.h @@ -34,6 +34,14 @@ enum { // This is bad and can lead to unpredictable memory corruptions, etc // because range access functions assume linearity. kBrokenLinearity = 1 << 2, + // Meta for an app region overlaps with the meta of another app region. + // This is determined by recomputing the individual meta regions for + // each app region. + // + // N.B. There is no "kBrokenReverseMetaMapping" constant because there + // is no MetaToMem function. However, note that (!kBrokenLinearity + // && !kBrokenAliasedMetas) implies that MemToMeta is invertible. + kBrokenAliasedMetas = 1 << 3, }; /* @@ -159,6 +167,7 @@ C/C++ on linux/aarch64 (39-bit VMA) 7d00 0000 00 - 7fff ffff ff: modules and main thread stack */ struct MappingAarch64_39 { + static const uptr kBroken = kBrokenAliasedMetas; static const uptr kLoAppMemBeg = 0x0000001000ull; static const uptr kLoAppMemEnd = 0x0100000000ull; static const uptr kShadowBeg = 0x0400000000ull; @@ -191,7 +200,7 @@ C/C++ on linux/aarch64 (42-bit VMA) 3f000 0000 00 - 3ffff ffff ff: modules and main thread stack */ struct MappingAarch64_42 { - static const uptr kBroken = kBrokenReverseMapping; + static const uptr kBroken = kBrokenReverseMapping | kBrokenAliasedMetas; static const uptr kLoAppMemBeg = 0x00000001000ull; static const uptr kLoAppMemEnd = 0x01000000000ull; static const uptr kShadowBeg = 0x08000000000ull; @@ -274,8 +283,8 @@ C/C++ on linux/powerpc64 (44-bit VMA) 0f60 0000 0000 - 1000 0000 0000: modules and main thread stack */ struct MappingPPC64_44 { - static const uptr kBroken = - kBrokenMapping | kBrokenReverseMapping | kBrokenLinearity; + static const uptr kBroken = kBrokenMapping | kBrokenReverseMapping | + kBrokenLinearity | kBrokenAliasedMetas; static const uptr kMetaShadowBeg = 0x0b0000000000ull; static const uptr kMetaShadowEnd = 0x0d0000000000ull; static const uptr kShadowBeg = 0x000100000000ull; @@ -286,6 +295,7 @@ struct MappingPPC64_44 { static const uptr kHeapMemEnd = 0x0f5000000000ull; static const uptr kHiAppMemBeg = 0x0f6000000000ull; static const uptr kHiAppMemEnd = 0x100000000000ull; // 44 bits + static const uptr kShadowMsk = 0x0f0000000000ull; static const uptr kShadowXor = 0x002100000000ull; static const uptr kShadowAdd = 0x000000000000ull; diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cpp b/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cpp index ba49df7..78398ac 100644 --- a/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cpp +++ b/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cpp @@ -15,6 +15,11 @@ namespace __tsan { +struct Region { + uptr start; + uptr end; +}; + void CheckShadow(const Shadow *s, Sid sid, Epoch epoch, uptr addr, uptr size, AccessType typ) { uptr addr1 = 0; @@ -126,6 +131,30 @@ bool broken(uptr what, typename Has::Result = false) { return Mapping::kBroken & what; } +static int CompareRegion(const void *region_a, const void *region_b) { + uptr start_a = ((struct Region *)region_a)->start; + uptr start_b = ((struct Region *)region_b)->start; + + if (start_a < start_b) { + return -1; + } else if (start_a > start_b) { + return 1; + } else { + return 0; + } +} + +template +static void AddMetaRegion(struct Region *shadows, int *num_regions, uptr start, + uptr end) { + // If the app region is not empty, add its meta to the array. + if (start != end) { + shadows[*num_regions].start = (uptr)MemToMetaImpl::Apply(start); + shadows[*num_regions].end = (uptr)MemToMetaImpl::Apply(end - 1); + *num_regions = (*num_regions) + 1; + } +} + struct MappingTest { template static void Apply() { @@ -135,6 +164,11 @@ struct MappingTest { TestRegion(Mapping::kMidAppMemBeg, Mapping::kMidAppMemEnd); TestRegion(Mapping::kHiAppMemBeg, Mapping::kHiAppMemEnd); TestRegion(Mapping::kHeapMemBeg, Mapping::kHeapMemEnd); + + TestDisjointMetas(); + + // Not tested: the ordering of regions (low app vs. shadow vs. mid app + // etc.). That is enforced at runtime by CheckAndProtect. } template @@ -172,6 +206,41 @@ struct MappingTest { } } } + + template + static void TestDisjointMetas() { + // Checks that the meta for each app region does not overlap with + // the meta for other app regions. For example, the meta for a high + // app pointer shouldn't be aliased to the meta of a mid app pointer. + // Notice that this is important even though there does not exist a + // MetaToMem function. + // (If a MetaToMem function did exist, we could simply + // check in the TestRegion function that it inverts MemToMeta.) + // + // We don't try to be clever by allowing the non-PIE (low app) + // and PIE (mid and high app) meta regions to overlap. + struct Region metas[4]; + int num_regions = 0; + AddMetaRegion(metas, &num_regions, Mapping::kLoAppMemBeg, + Mapping::kLoAppMemEnd); + AddMetaRegion(metas, &num_regions, Mapping::kMidAppMemBeg, + Mapping::kMidAppMemEnd); + AddMetaRegion(metas, &num_regions, Mapping::kHiAppMemBeg, + Mapping::kHiAppMemEnd); + AddMetaRegion(metas, &num_regions, Mapping::kHeapMemBeg, + Mapping::kHeapMemEnd); + + // It is not required that the low app shadow is below the mid app + // shadow etc., hence we sort the shadows. + qsort(metas, num_regions, sizeof(struct Region), CompareRegion); + + for (int i = 0; i < num_regions; i++) + Printf("[0x%lu, 0x%lu]\n", metas[i].start, metas[i].end); + + if (!broken(kBrokenAliasedMetas)) + for (int i = 1; i < num_regions; i++) + CHECK(metas[i - 1].end <= metas[i].start); + } }; TEST(Shadow, AllMappings) { ForEachMapping(); } -- 2.7.4