Skip to content

Commit dd1b7b7

Browse files
Advenam Tacetvitalybuka
Advenam Tacet
authored andcommitted
[1b/3][ASan][compiler-rt] API for annotating objects memory
This revision is a part of a series of patches extending AddressSanitizer C++ container overflow detection capabilities by adding annotations, similar to those existing in std::vector, to std::string and std::deque collections. These changes allow ASan to detect cases when the instrumented program accesses memory which is internally allocated by the collection but is still not in-use (accesses before or after the stored elements for std::deque, or between the size and capacity bounds for std::string). The motivation for the research and those changes was a bug, found by Trail of Bits, in a real code where an out-of-bounds read could happen as two strings were compared via a std::equals function that took iter1_begin, iter1_end, iter2_begin iterators (with a custom comparison function). When object iter1 was longer than iter2, read out-of-bounds on iter2 could happen. Container sanitization would detect it. This revision extends a compiler-rt ASan sanitization API function sanitizer_annotate_contiguous_container used to sanitize/annotate containers like std::vector to support different allocators and situations when granules are shared between objects. Those changes are necessary to support annotating objects' self memory (in contrast to annotating memory allocated by an object) like short std::basic_string (with short string optimization). That also allows use of non-standard memory allocators, as alignment requirement is no longer necessary. This also updates an API function to verify if a double ended contiguous container is correctly annotated (__sanitizer_verify_contiguous_container). If you have any questions, please email: [email protected] [email protected] Reviewed By: #sanitizers, vitalybuka Differential Revision: https://reviews.llvm.org/D132522
1 parent 5193d0a commit dd1b7b7

File tree

3 files changed

+142
-33
lines changed

3 files changed

+142
-33
lines changed

compiler-rt/lib/asan/asan_poisoning.cpp

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -382,15 +382,64 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p,
382382
uptr old_mid = reinterpret_cast<uptr>(old_mid_p);
383383
uptr new_mid = reinterpret_cast<uptr>(new_mid_p);
384384
uptr granularity = ASAN_SHADOW_GRANULARITY;
385-
if (!(beg <= old_mid && beg <= new_mid && old_mid <= end && new_mid <= end &&
386-
IsAligned(beg, granularity))) {
385+
if (!(beg <= old_mid && beg <= new_mid && old_mid <= end && new_mid <= end)) {
387386
GET_STACK_TRACE_FATAL_HERE;
388387
ReportBadParamsToAnnotateContiguousContainer(beg, end, old_mid, new_mid,
389388
&stack);
390389
}
391390
CHECK_LE(end - beg,
392391
FIRST_32_SECOND_64(1UL << 30, 1ULL << 40)); // Sanity check.
393392

393+
if (old_mid == new_mid)
394+
return; // Nothing to do here.
395+
396+
// Handle misaligned end and cut it off.
397+
if (UNLIKELY(!AddrIsAlignedByGranularity(end))) {
398+
uptr end_down = RoundDownTo(end, granularity);
399+
// Either new or old mid must be in the granule to affect it.
400+
if (new_mid > end_down) {
401+
if (AddressIsPoisoned(end)) {
402+
*(u8 *)MemToShadow(end_down) = static_cast<u8>(new_mid - end_down);
403+
} else {
404+
// Something after the container - don't touch.
405+
}
406+
} else if (old_mid > end_down) {
407+
if (AddressIsPoisoned(end)) {
408+
*(u8 *)MemToShadow(end_down) = kAsanContiguousContainerOOBMagic;
409+
} else {
410+
// Something after the container - don't touch.
411+
}
412+
}
413+
414+
if (beg >= end_down)
415+
return; // Same granule.
416+
417+
old_mid = Min(end_down, old_mid);
418+
new_mid = Min(end_down, new_mid);
419+
}
420+
421+
// Handle misaligned begin and cut it off.
422+
if (UNLIKELY(!AddrIsAlignedByGranularity(beg))) {
423+
uptr beg_up = RoundUpTo(beg, granularity);
424+
uptr beg_down = RoundDownTo(beg, granularity);
425+
// As soon as we add first byte into container we will not be able to
426+
// determine the state of the byte before the container. So we assume it's
427+
// always unpoison.
428+
429+
// Either new or old mid must be in the granule to affect it.
430+
if (new_mid < beg_up) {
431+
*(u8 *)MemToShadow(beg_down) = static_cast<u8>(new_mid - beg_down);
432+
} else if (old_mid < beg_up) {
433+
*(u8 *)MemToShadow(beg_down) = 0;
434+
}
435+
436+
old_mid = Max(beg_up, old_mid);
437+
new_mid = Max(beg_up, new_mid);
438+
}
439+
440+
if (old_mid == new_mid)
441+
return;
442+
394443
uptr a = RoundDownTo(Min(old_mid, new_mid), granularity);
395444
uptr c = RoundUpTo(Max(old_mid, new_mid), granularity);
396445
uptr d1 = RoundDownTo(old_mid, granularity);
@@ -425,8 +474,13 @@ const void *__sanitizer_contiguous_container_find_bad_address(
425474
const void *beg_p, const void *mid_p, const void *end_p) {
426475
if (!flags()->detect_container_overflow)
427476
return nullptr;
477+
uptr granularity = ASAN_SHADOW_GRANULARITY;
428478
uptr beg = reinterpret_cast<uptr>(beg_p);
429479
uptr end = reinterpret_cast<uptr>(end_p);
480+
uptr annotations_end =
481+
(!AddrIsAlignedByGranularity(end) && !AddressIsPoisoned(end))
482+
? RoundDownTo(end, granularity)
483+
: end;
430484
uptr mid = reinterpret_cast<uptr>(mid_p);
431485
CHECK_LE(beg, mid);
432486
CHECK_LE(mid, end);
@@ -436,9 +490,9 @@ const void *__sanitizer_contiguous_container_find_bad_address(
436490
uptr r1_beg = beg;
437491
uptr r1_end = Min(beg + kMaxRangeToCheck, mid);
438492
uptr r2_beg = Max(beg, mid - kMaxRangeToCheck);
439-
uptr r2_end = Min(end, mid + kMaxRangeToCheck);
440-
uptr r3_beg = Max(end - kMaxRangeToCheck, mid);
441-
uptr r3_end = end;
493+
uptr r2_end = Min(annotations_end, mid + kMaxRangeToCheck);
494+
uptr r3_beg = Max(annotations_end - kMaxRangeToCheck, mid);
495+
uptr r3_end = annotations_end;
442496
for (uptr i = r1_beg; i < r1_end; i++)
443497
if (AddressIsPoisoned(i))
444498
return reinterpret_cast<const void *>(i);

compiler-rt/test/asan/TestCases/contiguous_container.cpp

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,83 @@
88
#include <assert.h>
99
#include <sanitizer/asan_interface.h>
1010

11-
void TestContainer(size_t capacity) {
12-
char *beg = new char[capacity];
11+
static constexpr size_t kGranularity = 8;
12+
13+
static constexpr bool AddrIsAlignedByGranularity(uintptr_t a) {
14+
return (a & (kGranularity - 1)) == 0;
15+
}
16+
17+
static constexpr uintptr_t RoundDown(uintptr_t x) {
18+
return x & ~(kGranularity - 1);
19+
}
20+
21+
void TestContainer(size_t capacity, size_t off_begin, size_t off_end,
22+
bool off_poisoned) {
23+
char *buffer = new char[capacity + off_begin + off_end];
24+
char *buffer_end = buffer + capacity + off_begin + off_end;
25+
if (off_poisoned)
26+
__sanitizer_annotate_contiguous_container(buffer, buffer_end, buffer_end,
27+
buffer);
28+
char *beg = buffer + off_begin;
1329
char *end = beg + capacity;
14-
char *mid = beg + capacity;
30+
char *mid = off_poisoned ? beg : beg + capacity;
1531
char *old_mid = 0;
32+
// If after the container, there is another object, last granule
33+
// cannot be poisoned.
34+
char *cannot_poison =
35+
(off_end == 0) ? end : (char *)RoundDown((uintptr_t)end);
1636

17-
for (int i = 0; i < 10000; i++) {
37+
for (int i = 0; i < 1000; i++) {
1838
size_t size = rand() % (capacity + 1);
1939
assert(size <= capacity);
2040
old_mid = mid;
2141
mid = beg + size;
2242
__sanitizer_annotate_contiguous_container(beg, end, old_mid, mid);
2343

44+
// If off buffer before the container was poisoned and we had to
45+
// unpoison it, we won't poison it again as we don't have information,
46+
// if it was poisoned.
47+
for (size_t idx = 0; idx < off_begin && !off_poisoned; idx++)
48+
assert(!__asan_address_is_poisoned(buffer + idx));
2449
for (size_t idx = 0; idx < size; idx++)
25-
assert(!__asan_address_is_poisoned(beg + idx));
26-
for (size_t idx = size; idx < capacity; idx++)
27-
assert(__asan_address_is_poisoned(beg + idx));
50+
assert(!__asan_address_is_poisoned(beg + idx));
51+
for (size_t idx = size; beg + idx < cannot_poison; idx++)
52+
assert(__asan_address_is_poisoned(beg + idx));
53+
for (size_t idx = 0; idx < off_end; idx++) {
54+
if (!off_poisoned)
55+
assert(!__asan_address_is_poisoned(end + idx));
56+
else // off part after the buffer should be always poisoned
57+
assert(__asan_address_is_poisoned(end + idx));
58+
}
59+
2860
assert(__sanitizer_verify_contiguous_container(beg, mid, end));
2961
assert(NULL ==
3062
__sanitizer_contiguous_container_find_bad_address(beg, mid, end));
31-
if (mid != beg) {
32-
assert(!__sanitizer_verify_contiguous_container(beg, mid - 1, end));
33-
assert(mid - 1 == __sanitizer_contiguous_container_find_bad_address(
34-
beg, mid - 1, end));
63+
size_t distance = (off_end > 0) ? kGranularity + 1 : 1;
64+
if (mid >= beg + distance) {
65+
assert(
66+
!__sanitizer_verify_contiguous_container(beg, mid - distance, end));
67+
assert(mid - distance ==
68+
__sanitizer_contiguous_container_find_bad_address(
69+
beg, mid - distance, end));
3570
}
36-
if (mid != end) {
37-
assert(!__sanitizer_verify_contiguous_container(beg, mid + 1, end));
71+
72+
if (mid + distance <= end) {
73+
assert(
74+
!__sanitizer_verify_contiguous_container(beg, mid + distance, end));
3875
assert(mid == __sanitizer_contiguous_container_find_bad_address(
39-
beg, mid + 1, end));
76+
beg, mid + distance, end));
4077
}
4178
}
4279

4380
// Don't forget to unpoison the whole thing before destroying/reallocating.
44-
__sanitizer_annotate_contiguous_container(beg, end, mid, end);
45-
for (size_t idx = 0; idx < capacity; idx++)
46-
assert(!__asan_address_is_poisoned(beg + idx));
47-
delete[] beg;
81+
if (capacity == 0 && off_poisoned)
82+
mid = buffer;
83+
__sanitizer_annotate_contiguous_container(buffer, buffer_end, mid,
84+
buffer_end);
85+
for (size_t idx = 0; idx < capacity + off_begin + off_end; idx++)
86+
assert(!__asan_address_is_poisoned(buffer + idx));
87+
delete[] buffer;
4888
}
4989

5090
__attribute__((noinline))
@@ -54,7 +94,7 @@ __attribute__((noinline))
5494
void ThrowAndCatch() {
5595
try {
5696
Throw();
57-
} catch(...) {
97+
} catch (...) {
5898
}
5999
}
60100

@@ -72,8 +112,11 @@ void TestThrow() {
72112
}
73113

74114
int main(int argc, char **argv) {
75-
int n = argc == 1 ? 128 : atoi(argv[1]);
115+
int n = argc == 1 ? 64 : atoi(argv[1]);
76116
for (int i = 0; i <= n; i++)
77-
TestContainer(i);
117+
for (int j = 0; j < 8; j++)
118+
for (int k = 0; k < 8; k++)
119+
for (int off = 0; off < 2; ++off)
120+
TestContainer(i, j, k, off);
78121
TestThrow();
79122
}

compiler-rt/test/asan/TestCases/contiguous_container_crash.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// RUN: %clangxx_asan -O %s -o %t
22
// RUN: not %run %t crash 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s
33
// RUN: not %run %t bad-bounds 2>&1 | FileCheck --check-prefix=CHECK-BAD-BOUNDS %s
4-
// RUN: not %run %t bad-alignment 2>&1 | FileCheck --check-prefix=CHECK-BAD-ALIGNMENT %s
4+
// RUN: not %run %t odd-alignment 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s
5+
// RUN: not %run %t odd-alignment-end 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s
56
// RUN: %env_asan_opts=detect_container_overflow=0 %run %t crash
67
//
78
// Test crash due to __sanitizer_annotate_contiguous_container.
@@ -34,12 +35,20 @@ void BadBounds() {
3435
&t[0] + 50);
3536
}
3637

37-
void BadAlignment() {
38+
int OddAlignment() {
3839
int t[100];
39-
// CHECK-BAD-ALIGNMENT: ERROR: AddressSanitizer: bad parameters to __sanitizer_annotate_contiguous_container
40-
// CHECK-BAD-ALIGNMENT: ERROR: beg is not aligned by {{[0-9]+}}
41-
__sanitizer_annotate_contiguous_container(&t[1], &t[0] + 100, &t[1] + 10,
40+
t[60] = 0;
41+
__sanitizer_annotate_contiguous_container(&t[1], &t[0] + 100, &t[0] + 100,
42+
&t[1] + 50);
43+
return (int)t[60 * one]; // Touches the poisoned memory.
44+
}
45+
46+
int OddAlignmentEnd() {
47+
int t[99];
48+
t[60] = 0;
49+
__sanitizer_annotate_contiguous_container(&t[0], &t[0] + 98, &t[0] + 98,
4250
&t[0] + 50);
51+
return (int)t[60 * one]; // Touches the poisoned memory.
4352
}
4453

4554
int main(int argc, char **argv) {
@@ -48,6 +57,9 @@ int main(int argc, char **argv) {
4857
return TestCrash();
4958
else if (!strcmp(argv[1], "bad-bounds"))
5059
BadBounds();
51-
else if (!strcmp(argv[1], "bad-alignment"))
52-
BadAlignment();
60+
else if (!strcmp(argv[1], "odd-alignment"))
61+
return OddAlignment();
62+
else if (!strcmp(argv[1], "odd-alignment-end"))
63+
return OddAlignmentEnd();
64+
return 0;
5365
}

0 commit comments

Comments
 (0)