1 | //===-- asan_mem_test.cpp -------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is a part of AddressSanitizer, an address sanity checker. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | #include <string.h> |
13 | #include "asan_test_utils.h" |
14 | #if defined(_GNU_SOURCE) |
15 | #include <strings.h> // for bcmp |
16 | #endif |
17 | #include <vector> |
18 | |
19 | template<typename T> |
20 | void MemSetOOBTestTemplate(size_t length) { |
21 | if (length == 0) return; |
22 | size_t size = Ident(sizeof(T) * length); |
23 | T *array = Ident((T*)malloc(size: size)); |
24 | int element = Ident(42); |
25 | int zero = Ident(0); |
26 | void *(*MEMSET)(void *s, int c, size_t n) = Ident(memset); |
27 | // memset interval inside array |
28 | MEMSET(array, element, size); |
29 | MEMSET(array, element, size - 1); |
30 | MEMSET(array + length - 1, element, sizeof(T)); |
31 | MEMSET(array, element, 1); |
32 | |
33 | // memset 0 bytes |
34 | MEMSET(array - 10, element, zero); |
35 | MEMSET(array - 1, element, zero); |
36 | MEMSET(array, element, zero); |
37 | MEMSET(array + length, 0, zero); |
38 | MEMSET(array + length + 1, 0, zero); |
39 | |
40 | // try to memset bytes after array |
41 | EXPECT_DEATH(MEMSET(array, 0, size + 1), |
42 | RightOOBWriteMessage(0)); |
43 | EXPECT_DEATH(MEMSET((char*)(array + length) - 1, element, 6), |
44 | RightOOBWriteMessage(0)); |
45 | EXPECT_DEATH(MEMSET(array + 1, element, size + sizeof(T)), |
46 | RightOOBWriteMessage(0)); |
47 | // whole interval is after |
48 | EXPECT_DEATH(MEMSET(array + length + 1, 0, 10), |
49 | RightOOBWriteMessage(sizeof(T))); |
50 | |
51 | // try to memset bytes before array |
52 | EXPECT_DEATH(MEMSET((char*)array - 1, element, size), |
53 | LeftOOBWriteMessage(1)); |
54 | EXPECT_DEATH(MEMSET((char*)array - 5, 0, 6), |
55 | LeftOOBWriteMessage(5)); |
56 | if (length >= 100) { |
57 | // Large OOB, we find it only if the redzone is large enough. |
58 | EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)), |
59 | LeftOOBWriteMessage(5 * sizeof(T))); |
60 | } |
61 | // whole interval is before |
62 | EXPECT_DEATH(MEMSET(array - 2, 0, sizeof(T)), |
63 | LeftOOBWriteMessage(2 * sizeof(T))); |
64 | |
65 | // try to memset bytes both before & after |
66 | EXPECT_DEATH(MEMSET((char*)array - 2, element, size + 4), |
67 | LeftOOBWriteMessage(2)); |
68 | |
69 | free(array); |
70 | } |
71 | |
72 | TEST(AddressSanitizer, MemSetOOBTest) { |
73 | MemSetOOBTestTemplate<char>(length: 100); |
74 | MemSetOOBTestTemplate<int>(length: 5); |
75 | MemSetOOBTestTemplate<double>(length: 256); |
76 | // We can test arrays of structres/classes here, but what for? |
77 | } |
78 | |
79 | // Try to allocate two arrays of 'size' bytes that are near each other. |
80 | // Strictly speaking we are not guaranteed to find such two pointers, |
81 | // but given the structure of asan's allocator we will. |
82 | static bool AllocateTwoAdjacentArrays(char **x1, char **x2, size_t size) { |
83 | std::vector<uintptr_t> v; |
84 | bool res = false; |
85 | for (size_t i = 0; i < 1000U && !res; i++) { |
86 | v.push_back(reinterpret_cast<uintptr_t>(new char[size])); |
87 | if (i == 0) continue; |
88 | sort(v.begin(), v.end()); |
89 | for (size_t j = 1; j < v.size(); j++) { |
90 | assert(v[j] > v[j-1]); |
91 | if ((size_t)(v[j] - v[j-1]) < size * 2) { |
92 | *x2 = reinterpret_cast<char*>(v[j]); |
93 | *x1 = reinterpret_cast<char*>(v[j-1]); |
94 | res = true; |
95 | break; |
96 | } |
97 | } |
98 | } |
99 | |
100 | for (size_t i = 0; i < v.size(); i++) { |
101 | char *p = reinterpret_cast<char *>(v[i]); |
102 | if (res && p == *x1) continue; |
103 | if (res && p == *x2) continue; |
104 | delete [] p; |
105 | } |
106 | return res; |
107 | } |
108 | |
109 | TEST(AddressSanitizer, LargeOOBInMemset) { |
110 | for (size_t size = 200; size < 100000; size += size / 2) { |
111 | char *x1, *x2; |
112 | if (!Ident(AllocateTwoAdjacentArrays)(&x1, &x2, size)) |
113 | continue; |
114 | // fprintf(stderr, " large oob memset: %p %p %zd\n", x1, x2, size); |
115 | // Do a memset on x1 with huge out-of-bound access that will end up in x2. |
116 | EXPECT_DEATH(Ident(memset)(x1, 0, size * 2), |
117 | "is located 0 bytes after" ); |
118 | delete [] x1; |
119 | delete [] x2; |
120 | return; |
121 | } |
122 | assert(0 && "Did not find two adjacent malloc-ed pointers" ); |
123 | } |
124 | |
125 | // Same test for memcpy and memmove functions |
126 | template <typename T, class M> |
127 | void MemTransferOOBTestTemplate(size_t length) { |
128 | if (length == 0) return; |
129 | size_t size = Ident(sizeof(T) * length); |
130 | T *src = Ident((T*)malloc(size: size)); |
131 | T *dest = Ident((T*)malloc(size: size)); |
132 | int zero = Ident(0); |
133 | |
134 | // valid transfer of bytes between arrays |
135 | M::transfer(dest, src, size); |
136 | M::transfer(dest + 1, src, size - sizeof(T)); |
137 | M::transfer(dest, src + length - 1, sizeof(T)); |
138 | M::transfer(dest, src, 1); |
139 | |
140 | // transfer zero bytes |
141 | M::transfer(dest - 1, src, 0); |
142 | M::transfer(dest + length, src, zero); |
143 | M::transfer(dest, src - 1, zero); |
144 | M::transfer(dest, src, zero); |
145 | |
146 | // try to change mem after dest |
147 | EXPECT_DEATH(M::transfer(dest + 1, src, size), |
148 | RightOOBWriteMessage(0)); |
149 | EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5), |
150 | RightOOBWriteMessage(0)); |
151 | |
152 | // try to change mem before dest |
153 | EXPECT_DEATH(M::transfer(dest - 2, src, size), |
154 | LeftOOBWriteMessage(2 * sizeof(T))); |
155 | EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4), |
156 | LeftOOBWriteMessage(3)); |
157 | |
158 | // try to access mem after src |
159 | EXPECT_DEATH(M::transfer(dest, src + 2, size), |
160 | RightOOBReadMessage(0)); |
161 | EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6), |
162 | RightOOBReadMessage(0)); |
163 | |
164 | // try to access mem before src |
165 | EXPECT_DEATH(M::transfer(dest, src - 1, size), |
166 | LeftOOBReadMessage(sizeof(T))); |
167 | EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7), |
168 | LeftOOBReadMessage(6)); |
169 | |
170 | // Generally we don't need to test cases where both accessing src and writing |
171 | // to dest address to poisoned memory. |
172 | |
173 | T *big_src = Ident((T*)malloc(size: size * 2)); |
174 | T *big_dest = Ident((T*)malloc(size: size * 2)); |
175 | // try to change mem to both sides of dest |
176 | EXPECT_DEATH(M::transfer(dest - 1, big_src, size * 2), |
177 | LeftOOBWriteMessage(sizeof(T))); |
178 | // try to access mem to both sides of src |
179 | EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2), |
180 | LeftOOBReadMessage(2 * sizeof(T))); |
181 | |
182 | free(src); |
183 | free(dest); |
184 | free(big_src); |
185 | free(big_dest); |
186 | } |
187 | |
188 | class MemCpyWrapper { |
189 | public: |
190 | static void* transfer(void *to, const void *from, size_t size) { |
191 | return Ident(memcpy)(to, from, size); |
192 | } |
193 | }; |
194 | |
195 | TEST(AddressSanitizer, MemCpyOOBTest) { |
196 | MemTransferOOBTestTemplate<char, MemCpyWrapper>(length: 100); |
197 | MemTransferOOBTestTemplate<int, MemCpyWrapper>(length: 1024); |
198 | } |
199 | |
200 | class MemMoveWrapper { |
201 | public: |
202 | static void* transfer(void *to, const void *from, size_t size) { |
203 | return Ident(memmove)(to, from, size); |
204 | } |
205 | }; |
206 | |
207 | TEST(AddressSanitizer, MemMoveOOBTest) { |
208 | MemTransferOOBTestTemplate<char, MemMoveWrapper>(length: 100); |
209 | MemTransferOOBTestTemplate<int, MemMoveWrapper>(length: 1024); |
210 | } |
211 | |
212 | template <int (*cmpfn)(const void *, const void *, size_t)> |
213 | void CmpOOBTestCommon() { |
214 | size_t size = Ident(100); |
215 | char *s1 = MallocAndMemsetString(size); |
216 | char *s2 = MallocAndMemsetString(size); |
217 | // Normal cmpfn calls. |
218 | Ident(cmpfn(s1, s2, size)); |
219 | Ident(cmpfn(s1 + size - 1, s2 + size - 1, 1)); |
220 | Ident(cmpfn(s1 - 1, s2 - 1, 0)); |
221 | // One of arguments points to not allocated memory. |
222 | EXPECT_DEATH(Ident(cmpfn)(s1 - 1, s2, 1), LeftOOBReadMessage(1)); |
223 | EXPECT_DEATH(Ident(cmpfn)(s1, s2 - 1, 1), LeftOOBReadMessage(1)); |
224 | EXPECT_DEATH(Ident(cmpfn)(s1 + size, s2, 1), RightOOBReadMessage(0)); |
225 | EXPECT_DEATH(Ident(cmpfn)(s1, s2 + size, 1), RightOOBReadMessage(0)); |
226 | // Hit unallocated memory and die. |
227 | EXPECT_DEATH(Ident(cmpfn)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0)); |
228 | EXPECT_DEATH(Ident(cmpfn)(s1 + size - 1, s2, 2), RightOOBReadMessage(0)); |
229 | // Zero bytes are not terminators and don't prevent from OOB. |
230 | s1[size - 1] = '\0'; |
231 | s2[size - 1] = '\0'; |
232 | EXPECT_DEATH(Ident(cmpfn)(s1, s2, size + 1), RightOOBReadMessage(0)); |
233 | |
234 | // Even if the buffers differ in the first byte, we still assume that |
235 | // cmpfn may access the whole buffer and thus reporting the overflow here: |
236 | s1[0] = 1; |
237 | s2[0] = 123; |
238 | EXPECT_DEATH(Ident(cmpfn)(s1, s2, size + 1), RightOOBReadMessage(0)); |
239 | |
240 | free(ptr: s1); |
241 | free(ptr: s2); |
242 | } |
243 | |
244 | TEST(AddressSanitizer, MemCmpOOBTest) { CmpOOBTestCommon<memcmp>(); } |
245 | |
246 | TEST(AddressSanitizer, BCmpOOBTest) { |
247 | #if (defined(__linux__) && !defined(__ANDROID__) && defined(_GNU_SOURCE)) || \ |
248 | defined(__NetBSD__) || defined(__FreeBSD__) |
249 | CmpOOBTestCommon<bcmp>(); |
250 | #endif |
251 | } |
252 | |