1 : // Copyright 2014 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/agent/asan/block.h"
16 :
17 : #include <algorithm>
18 :
19 : #include "base/hash.h"
20 : #include "base/logging.h"
21 : #include "syzygy/agent/asan/asan_runtime.h"
22 : #include "syzygy/agent/asan/stack_capture_cache.h"
23 : #include "syzygy/common/align.h"
24 :
25 : namespace agent {
26 : namespace asan {
27 :
28 : namespace {
29 :
30 : using ::common::IsAligned;
31 :
32 : // Declares a function that returns the maximum value representable by
33 : // the given bitfield.
34 : #define DECLARE_GET_MAX_BITFIELD_VALUE_FUNCTION(Type, FieldName) \
35 : size_t GetMaxValue ## Type ## _ ## FieldName() { \
36 : Type t = {}; \
37 : t.FieldName = 0; \
38 : --t.FieldName; \
39 : size_t value = t.FieldName; \
40 : return value; \
41 : }
42 E : DECLARE_GET_MAX_BITFIELD_VALUE_FUNCTION(BlockHeader, body_size);
43 : #undef DECLARE_GET_MAX_BITFIELD_VALUE_FUNCTION
44 :
45 E : const size_t kMaxBlockHeaderBodySize = GetMaxValueBlockHeader_body_size();
46 :
47 E : void InitializeBlockHeader(BlockInfo* block_info) {
48 E : DCHECK_NE(static_cast<BlockInfo*>(NULL), block_info);
49 E : DCHECK_NE(static_cast<BlockHeader*>(NULL), block_info->header);
50 E : ::memset(block_info->header, 0, sizeof(BlockHeader));
51 E : block_info->header->magic = kBlockHeaderMagic;
52 E : block_info->header->is_nested = block_info->is_nested;
53 E : block_info->header->has_header_padding = block_info->header_padding_size > 0;
54 : block_info->header->has_excess_trailer_padding =
55 E : block_info->trailer_padding_size > sizeof(uint32);
56 E : block_info->header->state = ALLOCATED_BLOCK;
57 E : block_info->header->body_size = block_info->body_size;
58 E : }
59 :
60 E : void InitializeBlockHeaderPadding(BlockInfo* block_info) {
61 E : DCHECK_NE(static_cast<BlockInfo*>(NULL), block_info);
62 E : if (block_info->header_padding_size == 0)
63 E : return;
64 E : DCHECK(IsAligned(block_info->header_padding_size, kShadowRatio));
65 : DCHECK(IsAligned(block_info->header_padding_size,
66 E : 2 * sizeof(uint32)));
67 :
68 : ::memset(block_info->header_padding + sizeof(uint32),
69 : kBlockHeaderPaddingByte,
70 E : block_info->header_padding_size - 2 * sizeof(uint32));
71 E : uint32* head = reinterpret_cast<uint32*>(block_info->header_padding);
72 : uint32* tail = reinterpret_cast<uint32*>(
73 : block_info->header_padding + block_info->header_padding_size -
74 E : sizeof(uint32));
75 E : *head = block_info->header_padding_size;
76 E : *tail = block_info->header_padding_size;
77 E : }
78 :
79 E : void InitializeBlockTrailerPadding(BlockInfo* block_info) {
80 E : DCHECK_NE(static_cast<BlockInfo*>(NULL), block_info);
81 E : if (block_info->trailer_padding_size == 0)
82 E : return;
83 : ::memset(block_info->trailer_padding, kBlockTrailerPaddingByte,
84 E : block_info->trailer_padding_size);
85 E : if (block_info->trailer_padding_size > (kShadowRatio / 2)) {
86 : // This is guaranteed by kShadowRatio being >= 8, but we double check
87 : // for sanity's sake.
88 E : DCHECK_LE(sizeof(uint32), block_info->trailer_padding_size);
89 E : uint32* head = reinterpret_cast<uint32*>(block_info->trailer_padding);
90 E : *head = block_info->trailer_padding_size;
91 : }
92 E : }
93 :
94 E : void InitializeBlockTrailer(BlockInfo* block_info) {
95 E : DCHECK_NE(static_cast<BlockInfo*>(NULL), block_info);
96 E : ::memset(block_info->trailer, 0, sizeof(BlockTrailer));
97 E : block_info->trailer->alloc_ticks = ::GetTickCount();
98 E : block_info->trailer->alloc_tid = ::GetCurrentThreadId();
99 E : }
100 :
101 : // Combines the bits of a uint32 into the number of bits used to store the
102 : // block checksum.
103 E : uint32 CombineUInt32IntoBlockChecksum(uint32 val) {
104 E : uint32 checksum = 0;
105 E : while (val != 0) {
106 E : checksum ^= val;
107 E : val >>= kBlockHeaderChecksumBits;
108 E : }
109 E : checksum &= ((1 << kBlockHeaderChecksumBits) - 1);
110 E : return checksum;
111 E : }
112 :
113 : // An exception filter that catches access violations and out of bound accesses.
114 E : DWORD BadMemoryAccessFilter(EXCEPTION_POINTERS* e) {
115 : if (e->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
116 E : e->ExceptionRecord->ExceptionCode == EXCEPTION_ARRAY_BOUNDS_EXCEEDED) {
117 E : return EXCEPTION_EXECUTE_HANDLER;
118 : }
119 i : return EXCEPTION_CONTINUE_SEARCH;
120 E : }
121 :
122 : bool BlockInfoFromMemoryImpl(const void* const_raw_block,
123 E : CompactBlockInfo* block_info) {
124 E : DCHECK_NE(static_cast<void*>(NULL), const_raw_block);
125 E : DCHECK_NE(static_cast<CompactBlockInfo*>(NULL), block_info);
126 :
127 E : void* raw_block = const_cast<void*>(const_raw_block);
128 :
129 : // The raw_block header must be minimally aligned and begin with the expected
130 : // magic.
131 E : if (!IsAligned(reinterpret_cast<uint32>(raw_block), kShadowRatio))
132 E : return false;
133 E : BlockHeader* header = reinterpret_cast<BlockHeader*>(raw_block);
134 E : if (header->magic != kBlockHeaderMagic)
135 E : return false;
136 :
137 : // Parse the header padding if present.
138 E : uint32 header_padding_size = 0;
139 E : if (header->has_header_padding) {
140 E : uint8* padding = reinterpret_cast<uint8*>(header + 1);
141 E : uint32* head = reinterpret_cast<uint32*>(padding);
142 E : header_padding_size = *head;
143 E : if (header_padding_size < 2 * sizeof(uint32))
144 E : return false;
145 E : if (!IsAligned(header_padding_size, kShadowRatio))
146 i : return false;
147 : uint32* tail = reinterpret_cast<uint32*>(
148 E : padding + header_padding_size - sizeof(uint32));
149 E : if (*head != *tail)
150 E : return false;
151 : }
152 :
153 : // Parse the body.
154 E : uint8* body = reinterpret_cast<uint8*>(header + 1) + header_padding_size;
155 :
156 : // Parse the trailer padding.
157 E : uint32 trailer_padding_size = 0;
158 E : if (header->has_excess_trailer_padding) {
159 E : uint32* head = reinterpret_cast<uint32*>(body + header->body_size);
160 E : trailer_padding_size = *head;
161 E : } else if ((header->body_size % kShadowRatio) != (kShadowRatio / 2)) {
162 : trailer_padding_size = (kShadowRatio / 2) -
163 E : (header->body_size % (kShadowRatio / 2));
164 : }
165 :
166 : // Parse the trailer. The end of it must be 8 aligned.
167 : BlockTrailer* trailer = reinterpret_cast<BlockTrailer*>(
168 E : body + header->body_size + trailer_padding_size);
169 E : if (!IsAligned(reinterpret_cast<uint32>(trailer + 1), kShadowRatio))
170 i : return false;
171 :
172 E : block_info->block = reinterpret_cast<uint8*>(raw_block);
173 : block_info->block_size = reinterpret_cast<uint8*>(trailer + 1)
174 E : - reinterpret_cast<uint8*>(header);
175 E : block_info->header_size = sizeof(BlockHeader) + header_padding_size;
176 E : block_info->trailer_size = trailer_padding_size + sizeof(BlockTrailer);
177 E : block_info->is_nested = header->is_nested;
178 :
179 E : return true;
180 E : }
181 :
182 E : BlockHeader* BlockGetHeaderFromBodyImpl(const void* const_body) {
183 E : DCHECK_NE(static_cast<void*>(NULL), const_body);
184 :
185 E : void* body = const_cast<void*>(const_body);
186 :
187 : // The header must be appropriately aligned.
188 E : if (!IsAligned(reinterpret_cast<uint32>(body), kShadowRatio))
189 E : return NULL;
190 :
191 : // First assume that there is no padding, and check if a valid block header
192 : // is found there.
193 E : BlockHeader* header = reinterpret_cast<BlockHeader*>(body) - 1;
194 E : if (header->magic == kBlockHeaderMagic && header->has_header_padding == 0)
195 E : return header;
196 :
197 : // Otherwise assume there is padding. The padding must be formatted
198 : // correctly and have a valid length.
199 E : uint32* tail = reinterpret_cast<uint32*>(body) - 1;
200 E : if (*tail == 0 || !IsAligned(*tail, kShadowRatio))
201 E : return NULL;
202 E : uint32* head = (tail + 1) - ((*tail) / sizeof(uint32));
203 E : if (head > tail)
204 E : return NULL;
205 E : if (*head != *tail)
206 E : return NULL;
207 :
208 : // Expect there to be a valid block header.
209 E : header = reinterpret_cast<BlockHeader*>(head) - 1;
210 E : if (header->magic == kBlockHeaderMagic && header->has_header_padding == 1)
211 E : return header;
212 :
213 : // No valid block header was found before the provided body address.
214 E : return NULL;
215 E : }
216 :
217 : } // namespace
218 :
219 : bool BlockPlanLayout(size_t chunk_size,
220 : size_t alignment,
221 : size_t size,
222 : size_t min_left_redzone_size,
223 : size_t min_right_redzone_size,
224 E : BlockLayout* layout) {
225 E : DCHECK_LE(kShadowRatio, chunk_size);
226 E : DCHECK(::common::IsPowerOfTwo(chunk_size));
227 E : DCHECK_LE(kShadowRatio, alignment);
228 E : DCHECK_GE(chunk_size, alignment);
229 E : DCHECK(::common::IsPowerOfTwo(alignment));
230 :
231 : // Calculate minimum redzone sizes that respect the parameters.
232 : size_t left_redzone_size = ::common::AlignUp(
233 : std::max(min_left_redzone_size, sizeof(BlockHeader)),
234 E : alignment);
235 : size_t right_redzone_size = std::max(min_right_redzone_size,
236 E : sizeof(BlockTrailer));
237 :
238 : // Calculate the total size of the allocation.
239 : size_t total_size = ::common::AlignUp(
240 E : left_redzone_size + size + right_redzone_size, chunk_size);
241 :
242 E : if (total_size < size)
243 E : return false;
244 :
245 : // Now figure out the sizes of things such that the body of the allocation is
246 : // aligned as close as possible to the beginning of the right redzone while
247 : // respecting the body alignment requirements. This favors catching overflows
248 : // vs underflows when page protection mechanisms are active.
249 E : size_t body_trailer_size = size + right_redzone_size;
250 : size_t body_trailer_size_aligned = ::common::AlignUp(body_trailer_size,
251 E : alignment);
252 E : size_t body_padding_size = body_trailer_size_aligned - body_trailer_size;
253 E : right_redzone_size += body_padding_size;
254 :
255 : // The left redzone takes up the rest of the space.
256 E : left_redzone_size = total_size - right_redzone_size - size;
257 :
258 : // Make sure the basic layout invariants are satisfied.
259 E : DCHECK_LE(min_left_redzone_size, left_redzone_size);
260 E : DCHECK_LE(min_right_redzone_size, right_redzone_size);
261 E : DCHECK_EQ(total_size, (left_redzone_size + size + right_redzone_size));
262 E : DCHECK(IsAligned(total_size, chunk_size));
263 E : DCHECK(IsAligned(left_redzone_size, alignment));
264 :
265 : // Fill out the layout structure.
266 E : layout->block_alignment = chunk_size;
267 E : layout->block_size = total_size;
268 E : layout->header_size = sizeof(BlockHeader);
269 E : layout->header_padding_size = left_redzone_size - sizeof(BlockHeader);
270 E : layout->body_size = size;
271 E : layout->trailer_padding_size = right_redzone_size - sizeof(BlockTrailer);
272 E : layout->trailer_size = sizeof(BlockTrailer);
273 E : return true;
274 E : }
275 :
276 : void BlockInitialize(const BlockLayout& layout,
277 : void* allocation,
278 : bool is_nested,
279 E : BlockInfo* block_info) {
280 E : DCHECK_NE(static_cast<void*>(NULL), allocation);
281 : DCHECK(IsAligned(reinterpret_cast<uint32>(allocation),
282 E : layout.block_alignment));
283 :
284 : // If no output structure is provided then use a local one. We need the data
285 : // locally, but the caller might not be interested in it.
286 E : BlockInfo local_block_info = {};
287 E : if (block_info == NULL) {
288 i : block_info = &local_block_info;
289 i : } else {
290 E : ::memset(block_info, 0, sizeof(BlockInfo));
291 : }
292 :
293 : // Get pointers to the various components of the block.
294 E : uint8* cursor = reinterpret_cast<uint8*>(allocation);
295 E : block_info->block = reinterpret_cast<uint8*>(cursor);
296 E : block_info->block_size = layout.block_size;
297 E : block_info->is_nested = is_nested;
298 E : block_info->header = reinterpret_cast<BlockHeader*>(cursor);
299 E : cursor += sizeof(BlockHeader);
300 E : block_info->header_padding = cursor;
301 E : cursor += layout.header_padding_size;
302 E : block_info->header_padding_size = layout.header_padding_size;
303 E : block_info->body = reinterpret_cast<uint8*>(cursor);
304 E : cursor += layout.body_size;
305 E : block_info->body_size = layout.body_size;
306 E : block_info->trailer_padding = cursor;
307 E : cursor += layout.trailer_padding_size;
308 E : block_info->trailer_padding_size = layout.trailer_padding_size;
309 E : block_info->trailer = reinterpret_cast<BlockTrailer*>(cursor);
310 :
311 : // Indicates if the block is nested.
312 E : block_info->is_nested = is_nested;
313 :
314 : // If the block information is being returned to the user then determine
315 : // the extents of whole pages within it.
316 E : if (block_info != &local_block_info)
317 E : BlockIdentifyWholePages(block_info);
318 :
319 : // Initialize the various portions of the memory. The body is not initialized
320 : // as this is an unnecessary performance hit.
321 E : InitializeBlockHeader(block_info);
322 E : InitializeBlockHeaderPadding(block_info);
323 E : InitializeBlockTrailerPadding(block_info);
324 E : InitializeBlockTrailer(block_info);
325 E : }
326 :
327 i : bool BlockInfoFromMemory(const void* raw_block, CompactBlockInfo* block_info) {
328 i : DCHECK_NE(static_cast<void*>(NULL), raw_block);
329 i : DCHECK_NE(static_cast<CompactBlockInfo*>(NULL), block_info);
330 :
331 i : __try {
332 : // As little code as possible is inside the body of the __try so that
333 : // our code coverage can instrument it.
334 i : bool result = BlockInfoFromMemoryImpl(raw_block, block_info);
335 i : return result;
336 i : } __except (BadMemoryAccessFilter(GetExceptionInformation())) { // NOLINT
337 : // The block is either corrupt, or the pages are protected.
338 i : return false;
339 i : }
340 i : }
341 :
342 E : void ConvertBlockInfo(const CompactBlockInfo& compact, BlockInfo* expanded) {
343 E : expanded->block = compact.block;
344 E : expanded->block_size = compact.block_size;
345 E : expanded->header = reinterpret_cast<BlockHeader*>(compact.block);
346 E : expanded->header_padding_size = compact.header_size - sizeof(BlockHeader);
347 E : expanded->header_padding = compact.block + sizeof(BlockHeader);
348 E : expanded->body = compact.block + compact.header_size;
349 : expanded->body_size = compact.block_size - compact.header_size -
350 E : compact.trailer_size;
351 E : expanded->trailer_padding_size = compact.trailer_size - sizeof(BlockTrailer);
352 E : expanded->trailer_padding = expanded->body + expanded->body_size;
353 : expanded->trailer = reinterpret_cast<BlockTrailer*>(
354 E : expanded->trailer_padding + expanded->trailer_padding_size);
355 E : expanded->is_nested = compact.is_nested;
356 E : BlockIdentifyWholePages(expanded);
357 E : }
358 :
359 E : void ConvertBlockInfo(const BlockInfo& expanded, CompactBlockInfo* compact) {
360 E : DCHECK_NE(static_cast<CompactBlockInfo*>(nullptr), compact);
361 E : compact->block = expanded.block;
362 E : compact->block_size = expanded.block_size;
363 E : compact->header_size = sizeof(BlockHeader) + expanded.header_padding_size;
364 E : compact->trailer_size = sizeof(BlockTrailer) + expanded.trailer_padding_size;
365 E : compact->is_nested = expanded.is_nested;
366 E : }
367 :
368 E : bool BlockInfoFromMemory(const void* raw_block, BlockInfo* block_info) {
369 E : DCHECK_NE(static_cast<void*>(NULL), raw_block);
370 E : DCHECK_NE(static_cast<BlockInfo*>(NULL), block_info);
371 E : CompactBlockInfo compact = {};
372 E : if (!BlockInfoFromMemory(raw_block, &compact))
373 E : return false;
374 E : ConvertBlockInfo(compact, block_info);
375 E : return true;
376 E : }
377 :
378 i : BlockHeader* BlockGetHeaderFromBody(const void* body) {
379 i : DCHECK_NE(static_cast<void*>(NULL), body);
380 :
381 i : __try {
382 : // As little code as possible is inside the body of the __try so that
383 : // our code coverage can instrument it.
384 i : BlockHeader* header = BlockGetHeaderFromBodyImpl(body);
385 i : return header;
386 i : } __except (BadMemoryAccessFilter(GetExceptionInformation())) { // NOLINT
387 : // The block is either corrupt, or the pages are protected.
388 i : return NULL;
389 i : }
390 i : }
391 :
392 E : uint32 BlockCalculateChecksum(const BlockInfo& block_info) {
393 : // It is much easier to calculate the checksum in place so this actually
394 : // causes the block to be modified, but restores the original value.
395 E : uint32 old_checksum = block_info.header->checksum;
396 E : block_info.header->checksum = 0;
397 E : BlockSetChecksum(block_info);
398 E : uint32 new_checksum = block_info.header->checksum;
399 E : block_info.header->checksum = old_checksum;
400 E : return new_checksum;
401 E : }
402 :
403 E : bool BlockChecksumIsValid(const BlockInfo& block_info) {
404 E : uint32 checksum = BlockCalculateChecksum(block_info);
405 E : if (checksum == block_info.header->checksum)
406 E : return true;
407 E : return false;
408 E : }
409 :
410 E : void BlockSetChecksum(const BlockInfo& block_info) {
411 E : block_info.header->checksum = 0;
412 :
413 E : uint32 checksum = 0;
414 E : switch (block_info.header->state) {
415 : case ALLOCATED_BLOCK: {
416 : // Only checksum the header and trailer regions.
417 : checksum = base::SuperFastHash(
418 : reinterpret_cast<const char*>(block_info.block),
419 E : block_info.body - block_info.block);
420 : checksum ^= base::SuperFastHash(
421 : reinterpret_cast<const char*>(block_info.trailer_padding),
422 : block_info.block + block_info.block_size -
423 E : block_info.trailer_padding);
424 E : break;
425 : }
426 :
427 : // The checksum is the calculated in the same way in these two cases.
428 : // Similary, the catch all default case is calculated in this way so as to
429 : // allow the hash to successfully be calculated even for a block with a
430 : // corrupt state.
431 : case QUARANTINED_BLOCK:
432 : case FREED_BLOCK:
433 : default: {
434 : checksum = base::SuperFastHash(
435 : reinterpret_cast<const char*>(block_info.block),
436 E : block_info.block_size);
437 : break;
438 : }
439 : }
440 :
441 E : checksum = CombineUInt32IntoBlockChecksum(checksum);
442 E : DCHECK_EQ(0u, checksum >> kBlockHeaderChecksumBits);
443 E : block_info.header->checksum = checksum;
444 E : }
445 :
446 : // Identifies whole pages in the given block_info.
447 E : void BlockIdentifyWholePages(BlockInfo* block_info) {
448 E : DCHECK_NE(static_cast<BlockInfo*>(NULL), block_info);
449 : static const size_t kPageInfoSize =
450 : FIELD_OFFSET(BlockInfo, is_nested) -
451 : FIELD_OFFSET(BlockInfo, block_pages);
452 :
453 E : if (block_info->block_size < GetPageSize()) {
454 E : ::memset(&block_info->block_pages, 0, kPageInfoSize);
455 E : return;
456 : }
457 :
458 E : uint32 alloc_start = reinterpret_cast<uint32>(block_info->block);
459 E : uint32 alloc_end = alloc_start + block_info->block_size;
460 E : alloc_start = ::common::AlignUp(alloc_start, GetPageSize());
461 E : alloc_end = ::common::AlignDown(alloc_end, GetPageSize());
462 E : if (alloc_start >= alloc_end) {
463 E : ::memset(&block_info->block_pages, 0, kPageInfoSize);
464 E : return;
465 : }
466 :
467 E : block_info->block_pages = reinterpret_cast<uint8*>(alloc_start);
468 E : block_info->block_pages_size = alloc_end - alloc_start;
469 :
470 E : uint32 left_redzone_end = reinterpret_cast<uint32>(block_info->body);
471 E : uint32 right_redzone_start = left_redzone_end + block_info->body_size;
472 E : left_redzone_end = ::common::AlignDown(left_redzone_end, GetPageSize());
473 E : right_redzone_start = ::common::AlignUp(right_redzone_start, GetPageSize());
474 :
475 E : if (alloc_start < left_redzone_end) {
476 E : block_info->left_redzone_pages = reinterpret_cast<uint8*>(alloc_start);
477 E : block_info->left_redzone_pages_size = left_redzone_end - alloc_start;
478 E : } else {
479 E : block_info->left_redzone_pages = nullptr;
480 E : block_info->left_redzone_pages_size = 0;
481 : }
482 :
483 E : if (right_redzone_start < alloc_end) {
484 : block_info->right_redzone_pages =
485 E : reinterpret_cast<uint8*>(right_redzone_start);
486 E : block_info->right_redzone_pages_size = alloc_end - right_redzone_start;
487 E : } else {
488 E : block_info->right_redzone_pages = nullptr;
489 E : block_info->right_redzone_pages_size = 0;
490 : }
491 E : }
492 :
493 : // This namespace contains helpers for block analysis.
494 : namespace {
495 :
496 : // Determines if a stack-capture pointer is valid by referring to the
497 : // stack-capture cache in the active runtime.
498 E : bool IsValidStackCapturePointer(const common::StackCapture* stack) {
499 E : if (stack == nullptr)
500 E : return false;
501 E : AsanRuntime* runtime = AsanRuntime::runtime();
502 E : DCHECK_NE(static_cast<AsanRuntime*>(nullptr), runtime);
503 E : StackCaptureCache* cache = runtime->stack_cache();
504 E : DCHECK_NE(static_cast<StackCaptureCache*>(nullptr), cache);
505 E : if (!cache->StackCapturePointerIsValid(stack))
506 E : return false;
507 E : return true;
508 E : }
509 :
510 : // Determines if a thread-id is valid by referring to the cache of thread-ids
511 : // in the runtime.
512 E : bool IsValidThreadId(uint32 thread_id) {
513 E : AsanRuntime* runtime = AsanRuntime::runtime();
514 E : DCHECK_NE(static_cast<AsanRuntime*>(nullptr), runtime);
515 E : if (!runtime->ThreadIdIsValid(thread_id))
516 E : return false;
517 E : return true;
518 E : }
519 :
520 : // Determines if timestamp is plausible by referring to the process start
521 : // time as recorded by the runtime.
522 E : bool IsValidTicks(uint32 ticks) {
523 E : uint32 end = ::GetTickCount();
524 E : AsanRuntime* runtime = AsanRuntime::runtime();
525 E : DCHECK_NE(static_cast<AsanRuntime*>(nullptr), runtime);
526 E : uint32 begin = runtime->starting_ticks();
527 E : if (ticks < begin || ticks > end)
528 i : return false;
529 E : return true;
530 E : }
531 :
532 : // Determines if a heap id is valid by referring to the runtime.
533 E : bool IsValidHeapId(uint32 heap_id) {
534 E : AsanRuntime* runtime = AsanRuntime::runtime();
535 E : DCHECK_NE(static_cast<AsanRuntime*>(nullptr), runtime);
536 E : if (!runtime->HeapIdIsValid(heap_id))
537 E : return false;
538 E : return true;
539 E : }
540 :
541 E : bool BlockHeaderIsConsistent(const BlockInfo& block_info) {
542 E : const BlockHeader* h = block_info.header;
543 E : if (h->magic != kBlockHeaderMagic)
544 E : return false;
545 E : if (static_cast<bool>(h->is_nested) != block_info.is_nested)
546 i : return false;
547 :
548 E : bool expect_header_padding = block_info.header_padding_size > 0;
549 E : if (static_cast<bool>(h->has_header_padding) != expect_header_padding)
550 i : return false;
551 :
552 : bool expect_excess_trailer_padding =
553 E : block_info.trailer_padding_size > (kShadowRatio / 2);
554 : if (static_cast<bool>(h->has_excess_trailer_padding) !=
555 E : expect_excess_trailer_padding) {
556 i : return false;
557 : }
558 :
559 E : if (h->state > FREED_BLOCK)
560 E : return false;
561 :
562 E : if (h->body_size != block_info.body_size)
563 i : return false;
564 :
565 : // There should always be a valid allocation stack trace.
566 E : if (!IsValidStackCapturePointer(h->alloc_stack))
567 E : return false;
568 :
569 : // The free stack should be empty if we're in the allocated state.
570 E : if (h->state == ALLOCATED_BLOCK) {
571 E : if (h->free_stack != nullptr)
572 E : return false;
573 E : } else {
574 : // Otherwise there should be a valid free stack.
575 E : if (!IsValidStackCapturePointer(h->free_stack))
576 E : return false;
577 : }
578 :
579 : // If there's no header padding then the block is valid.
580 E : if (block_info.header_padding_size == 0)
581 E : return true;
582 :
583 : // Analyze the block header padding.
584 : const uint32* head = reinterpret_cast<const uint32*>(
585 E : block_info.header_padding);
586 : const uint32* tail = reinterpret_cast<const uint32*>(
587 E : block_info.header_padding + block_info.header_padding_size) - 1;
588 E : if (*head != block_info.header_padding_size)
589 i : return false;
590 E : if (*tail != block_info.header_padding_size)
591 E : return false;
592 : static const uint32 kHeaderPaddingValue =
593 : (kBlockHeaderPaddingByte << 24) |
594 : (kBlockHeaderPaddingByte << 16) |
595 : (kBlockHeaderPaddingByte << 8) |
596 : kBlockHeaderPaddingByte;
597 E : for (++head; head < tail; ++head) {
598 E : if (*head != kHeaderPaddingValue)
599 E : return false;
600 E : }
601 :
602 E : return true;
603 E : }
604 :
605 : // Returns true if the trailer is self-consistent, false otherwise.
606 : // Via |cross_consistent| indicates whether or not the header and trailer
607 : // are consistent with respect to each other.
608 E : bool BlockTrailerIsConsistent(const BlockInfo& block_info) {
609 E : const BlockHeader* h = block_info.header;
610 E : const BlockTrailer* t = block_info.trailer;
611 :
612 : // The allocation data must always be set.
613 E : if (!IsValidThreadId(t->alloc_tid))
614 E : return false;
615 E : if (!IsValidTicks(t->alloc_ticks))
616 i : return false;
617 :
618 : // The free fields must both be set, or both be clear.
619 E : if (t->free_tid != 0 && t->free_ticks != 0) {
620 E : if (!IsValidThreadId(t->free_tid))
621 i : return false;
622 E : if (!IsValidTicks(t->free_ticks))
623 i : return false;
624 E : } else if (t->free_tid != 0 || t->free_ticks != 0) {
625 : // If one or the other is set then the trailer is inconsistent.
626 i : return false;
627 : }
628 :
629 : // The heap ID must always be set and be valid.
630 E : if (!IsValidHeapId(t->heap_id))
631 E : return false;
632 :
633 : // If there's no padding to check then we're done.
634 E : if (block_info.trailer_padding_size == 0)
635 E : return true;
636 :
637 E : const uint8* padding = block_info.trailer_padding;
638 E : size_t size = block_info.trailer_padding_size;
639 :
640 : // If we have excess trailer padding then check the encoded length.
641 E : if (size > (kShadowRatio / 2)) {
642 E : const uint32* length = reinterpret_cast<const uint32*>(padding);
643 E : if (*length != size)
644 E : return false;
645 E : padding += sizeof(uint32);
646 E : size -= sizeof(uint32);
647 : }
648 :
649 : // Check the remaining trailer padding to ensure it's appropriately
650 : // flood-filled.
651 E : while (size > 0) {
652 E : if (*padding != kBlockTrailerPaddingByte)
653 E : return false;
654 E : ++padding;
655 E : --size;
656 E : }
657 :
658 E : return true;
659 E : }
660 :
661 : // Returns true if the header and trailer are cross-consistent with
662 : // respect to each other.
663 E : bool BlockHeaderAndTrailerAreCrossConsistent(const BlockInfo& block_info) {
664 E : const BlockHeader* h = block_info.header;
665 E : const BlockTrailer* t = block_info.trailer;
666 :
667 E : if (h->state == ALLOCATED_BLOCK) {
668 E : if (t->free_tid != 0 || t->free_ticks != 0)
669 i : return false;
670 E : } else {
671 E : if (t->free_tid == 0 || t->free_ticks == 0)
672 E : return false;
673 : }
674 :
675 E : return true;
676 E : }
677 :
678 : } // namespace
679 :
680 : void BlockAnalyze(const BlockInfo& block_info,
681 E : BlockAnalysisResult* result) {
682 E : DCHECK_NE(static_cast<BlockAnalysisResult*>(nullptr), result);
683 :
684 E : result->block_state = kDataStateUnknown;
685 E : result->header_state = kDataStateUnknown;
686 E : result->body_state = kDataStateUnknown;
687 E : result->trailer_state = kDataStateUnknown;
688 :
689 E : if (BlockChecksumIsValid(block_info)) {
690 E : result->block_state = kDataIsClean;
691 E : result->header_state = kDataIsClean;
692 E : result->body_state = kDataIsClean;
693 E : result->trailer_state = kDataIsClean;
694 E : return;
695 : }
696 :
697 : // At this point it's known that the checksum is invalid, so some part
698 : // of the block is corrupt.
699 E : result->block_state = kDataIsCorrupt;
700 :
701 : // Either the header, the body or the trailer is invalid. We can't
702 : // ever exonerate the body contents, so at the very least its state
703 : // is unknown. Leave it set to unknown.
704 :
705 : // Check the header.
706 E : bool consistent_header = BlockHeaderIsConsistent(block_info);
707 E : if (!consistent_header) {
708 E : result->header_state = kDataIsCorrupt;
709 E : } else {
710 E : result->header_state = kDataIsClean;
711 : }
712 :
713 : // Check the trailer.
714 E : bool consistent_trailer = BlockTrailerIsConsistent(block_info);
715 E : if (!consistent_trailer) {
716 E : result->trailer_state = kDataIsCorrupt;
717 E : } else {
718 E : result->trailer_state = kDataIsClean;
719 : }
720 :
721 E : bool cross_consistent = BlockHeaderAndTrailerAreCrossConsistent(block_info);
722 E : if (consistent_header && consistent_trailer) {
723 E : if (cross_consistent) {
724 : // If both the header and trailer are fine and cross-consistent, then the
725 : // body must be corrupt.
726 E : result->body_state = kDataIsCorrupt;
727 E : } else {
728 : // If both the header and trailer are fine but not cross-consistent, then
729 : // one or both of them is corrupt but we can't tell. Mark everything as
730 : // doubtful.
731 i : result->header_state = kDataStateUnknown;
732 i : result->trailer_state = kDataStateUnknown;
733 : }
734 : }
735 E : }
736 :
737 : } // namespace asan
738 : } // namespace agent
|