| //===-- asan_errors.h -------------------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file is a part of AddressSanitizer, an address sanity checker. |
| // |
| // ASan-private header for error structures. |
| //===----------------------------------------------------------------------===// |
| #ifndef ASAN_ERRORS_H |
| #define ASAN_ERRORS_H |
| |
| #include "asan_descriptions.h" |
| #include "asan_scariness_score.h" |
| #include "sanitizer_common/sanitizer_common.h" |
| |
| namespace __asan { |
| |
| // (*) VS2013 does not implement unrestricted unions, so we need a trivial |
| // default constructor explicitly defined for each particular error. |
| |
| // None of the error classes own the stack traces mentioned in them. |
| |
| struct ErrorBase { |
| ScarinessScoreBase scariness; |
| u32 tid; |
| |
| ErrorBase() = default; // (*) |
| explicit ErrorBase(u32 tid_) : tid(tid_) {} |
| ErrorBase(u32 tid_, int initial_score, const char *reason) : tid(tid_) { |
| scariness.Clear(); |
| scariness.Scare(initial_score, reason); |
| } |
| }; |
| |
| struct ErrorDeadlySignal : ErrorBase { |
| SignalContext signal; |
| |
| ErrorDeadlySignal() = default; // (*) |
| ErrorDeadlySignal(u32 tid, const SignalContext &sig) |
| : ErrorBase(tid), |
| signal(sig) { |
| scariness.Clear(); |
| if (signal.IsStackOverflow()) { |
| scariness.Scare(10, "stack-overflow"); |
| } else if (!signal.is_memory_access) { |
| scariness.Scare(10, "signal"); |
| } else if (signal.is_true_faulting_addr && |
| signal.addr < GetPageSizeCached()) { |
| scariness.Scare(10, "null-deref"); |
| } else if (signal.addr == signal.pc) { |
| scariness.Scare(60, "wild-jump"); |
| } else if (signal.write_flag == SignalContext::WRITE) { |
| scariness.Scare(30, "wild-addr-write"); |
| } else if (signal.write_flag == SignalContext::READ) { |
| scariness.Scare(20, "wild-addr-read"); |
| } else { |
| scariness.Scare(25, "wild-addr"); |
| } |
| } |
| void Print(); |
| }; |
| |
| struct ErrorDoubleFree : ErrorBase { |
| const BufferedStackTrace *second_free_stack; |
| HeapAddressDescription addr_description; |
| |
| ErrorDoubleFree() = default; // (*) |
| ErrorDoubleFree(u32 tid, BufferedStackTrace *stack, uptr addr) |
| : ErrorBase(tid, 42, "double-free"), |
| second_free_stack(stack) { |
| CHECK_GT(second_free_stack->size, 0); |
| GetHeapAddressInformation(addr, 1, &addr_description); |
| } |
| void Print(); |
| }; |
| |
| struct ErrorNewDeleteTypeMismatch : ErrorBase { |
| const BufferedStackTrace *free_stack; |
| HeapAddressDescription addr_description; |
| uptr delete_size; |
| uptr delete_alignment; |
| |
| ErrorNewDeleteTypeMismatch() = default; // (*) |
| ErrorNewDeleteTypeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr, |
| uptr delete_size_, uptr delete_alignment_) |
| : ErrorBase(tid, 10, "new-delete-type-mismatch"), |
| free_stack(stack), |
| delete_size(delete_size_), |
| delete_alignment(delete_alignment_) { |
| GetHeapAddressInformation(addr, 1, &addr_description); |
| } |
| void Print(); |
| }; |
| |
| struct ErrorFreeNotMalloced : ErrorBase { |
| const BufferedStackTrace *free_stack; |
| AddressDescription addr_description; |
| |
| ErrorFreeNotMalloced() = default; // (*) |
| ErrorFreeNotMalloced(u32 tid, BufferedStackTrace *stack, uptr addr) |
| : ErrorBase(tid, 40, "bad-free"), |
| free_stack(stack), |
| addr_description(addr, /*shouldLockThreadRegistry=*/false) {} |
| void Print(); |
| }; |
| |
| struct ErrorAllocTypeMismatch : ErrorBase { |
| const BufferedStackTrace *dealloc_stack; |
| AllocType alloc_type, dealloc_type; |
| AddressDescription addr_description; |
| |
| ErrorAllocTypeMismatch() = default; // (*) |
| ErrorAllocTypeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr, |
| AllocType alloc_type_, AllocType dealloc_type_) |
| : ErrorBase(tid, 10, "alloc-dealloc-mismatch"), |
| dealloc_stack(stack), |
| alloc_type(alloc_type_), |
| dealloc_type(dealloc_type_), |
| addr_description(addr, 1, false) {} |
| void Print(); |
| }; |
| |
| struct ErrorMallocUsableSizeNotOwned : ErrorBase { |
| const BufferedStackTrace *stack; |
| AddressDescription addr_description; |
| |
| ErrorMallocUsableSizeNotOwned() = default; // (*) |
| ErrorMallocUsableSizeNotOwned(u32 tid, BufferedStackTrace *stack_, uptr addr) |
| : ErrorBase(tid, 10, "bad-malloc_usable_size"), |
| stack(stack_), |
| addr_description(addr, /*shouldLockThreadRegistry=*/false) {} |
| void Print(); |
| }; |
| |
| struct ErrorSanitizerGetAllocatedSizeNotOwned : ErrorBase { |
| const BufferedStackTrace *stack; |
| AddressDescription addr_description; |
| |
| ErrorSanitizerGetAllocatedSizeNotOwned() = default; // (*) |
| ErrorSanitizerGetAllocatedSizeNotOwned(u32 tid, BufferedStackTrace *stack_, |
| uptr addr) |
| : ErrorBase(tid, 10, "bad-__sanitizer_get_allocated_size"), |
| stack(stack_), |
| addr_description(addr, /*shouldLockThreadRegistry=*/false) {} |
| void Print(); |
| }; |
| |
| struct ErrorCallocOverflow : ErrorBase { |
| const BufferedStackTrace *stack; |
| uptr count; |
| uptr size; |
| |
| ErrorCallocOverflow() = default; // (*) |
| ErrorCallocOverflow(u32 tid, BufferedStackTrace *stack_, uptr count_, |
| uptr size_) |
| : ErrorBase(tid, 10, "calloc-overflow"), |
| stack(stack_), |
| count(count_), |
| size(size_) {} |
| void Print(); |
| }; |
| |
| struct ErrorReallocArrayOverflow : ErrorBase { |
| const BufferedStackTrace *stack; |
| uptr count; |
| uptr size; |
| |
| ErrorReallocArrayOverflow() = default; // (*) |
| ErrorReallocArrayOverflow(u32 tid, BufferedStackTrace *stack_, uptr count_, |
| uptr size_) |
| : ErrorBase(tid, 10, "reallocarray-overflow"), |
| stack(stack_), |
| count(count_), |
| size(size_) {} |
| void Print(); |
| }; |
| |
| struct ErrorPvallocOverflow : ErrorBase { |
| const BufferedStackTrace *stack; |
| uptr size; |
| |
| ErrorPvallocOverflow() = default; // (*) |
| ErrorPvallocOverflow(u32 tid, BufferedStackTrace *stack_, uptr size_) |
| : ErrorBase(tid, 10, "pvalloc-overflow"), |
| stack(stack_), |
| size(size_) {} |
| void Print(); |
| }; |
| |
| struct ErrorInvalidAllocationAlignment : ErrorBase { |
| const BufferedStackTrace *stack; |
| uptr alignment; |
| |
| ErrorInvalidAllocationAlignment() = default; // (*) |
| ErrorInvalidAllocationAlignment(u32 tid, BufferedStackTrace *stack_, |
| uptr alignment_) |
| : ErrorBase(tid, 10, "invalid-allocation-alignment"), |
| stack(stack_), |
| alignment(alignment_) {} |
| void Print(); |
| }; |
| |
| struct ErrorInvalidAlignedAllocAlignment : ErrorBase { |
| const BufferedStackTrace *stack; |
| uptr size; |
| uptr alignment; |
| |
| ErrorInvalidAlignedAllocAlignment() = default; // (*) |
| ErrorInvalidAlignedAllocAlignment(u32 tid, BufferedStackTrace *stack_, |
| uptr size_, uptr alignment_) |
| : ErrorBase(tid, 10, "invalid-aligned-alloc-alignment"), |
| stack(stack_), |
| size(size_), |
| alignment(alignment_) {} |
| void Print(); |
| }; |
| |
| struct ErrorInvalidPosixMemalignAlignment : ErrorBase { |
| const BufferedStackTrace *stack; |
| uptr alignment; |
| |
| ErrorInvalidPosixMemalignAlignment() = default; // (*) |
| ErrorInvalidPosixMemalignAlignment(u32 tid, BufferedStackTrace *stack_, |
| uptr alignment_) |
| : ErrorBase(tid, 10, "invalid-posix-memalign-alignment"), |
| stack(stack_), |
| alignment(alignment_) {} |
| void Print(); |
| }; |
| |
| struct ErrorAllocationSizeTooBig : ErrorBase { |
| const BufferedStackTrace *stack; |
| uptr user_size; |
| uptr total_size; |
| uptr max_size; |
| |
| ErrorAllocationSizeTooBig() = default; // (*) |
| ErrorAllocationSizeTooBig(u32 tid, BufferedStackTrace *stack_, |
| uptr user_size_, uptr total_size_, uptr max_size_) |
| : ErrorBase(tid, 10, "allocation-size-too-big"), |
| stack(stack_), |
| user_size(user_size_), |
| total_size(total_size_), |
| max_size(max_size_) {} |
| void Print(); |
| }; |
| |
| struct ErrorRssLimitExceeded : ErrorBase { |
| const BufferedStackTrace *stack; |
| |
| ErrorRssLimitExceeded() = default; // (*) |
| ErrorRssLimitExceeded(u32 tid, BufferedStackTrace *stack_) |
| : ErrorBase(tid, 10, "rss-limit-exceeded"), |
| stack(stack_) {} |
| void Print(); |
| }; |
| |
| struct ErrorOutOfMemory : ErrorBase { |
| const BufferedStackTrace *stack; |
| uptr requested_size; |
| |
| ErrorOutOfMemory() = default; // (*) |
| ErrorOutOfMemory(u32 tid, BufferedStackTrace *stack_, uptr requested_size_) |
| : ErrorBase(tid, 10, "out-of-memory"), |
| stack(stack_), |
| requested_size(requested_size_) {} |
| void Print(); |
| }; |
| |
| struct ErrorStringFunctionMemoryRangesOverlap : ErrorBase { |
| const BufferedStackTrace *stack; |
| uptr length1, length2; |
| AddressDescription addr1_description; |
| AddressDescription addr2_description; |
| const char *function; |
| |
| ErrorStringFunctionMemoryRangesOverlap() = default; // (*) |
| ErrorStringFunctionMemoryRangesOverlap(u32 tid, BufferedStackTrace *stack_, |
| uptr addr1, uptr length1_, uptr addr2, |
| uptr length2_, const char *function_) |
| : ErrorBase(tid), |
| stack(stack_), |
| length1(length1_), |
| length2(length2_), |
| addr1_description(addr1, length1, /*shouldLockThreadRegistry=*/false), |
| addr2_description(addr2, length2, /*shouldLockThreadRegistry=*/false), |
| function(function_) { |
| char bug_type[100]; |
| internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function); |
| scariness.Clear(); |
| scariness.Scare(10, bug_type); |
| } |
| void Print(); |
| }; |
| |
| struct ErrorStringFunctionSizeOverflow : ErrorBase { |
| const BufferedStackTrace *stack; |
| AddressDescription addr_description; |
| uptr size; |
| |
| ErrorStringFunctionSizeOverflow() = default; // (*) |
| ErrorStringFunctionSizeOverflow(u32 tid, BufferedStackTrace *stack_, |
| uptr addr, uptr size_) |
| : ErrorBase(tid, 10, "negative-size-param"), |
| stack(stack_), |
| addr_description(addr, /*shouldLockThreadRegistry=*/false), |
| size(size_) {} |
| void Print(); |
| }; |
| |
| struct ErrorBadParamsToAnnotateContiguousContainer : ErrorBase { |
| const BufferedStackTrace *stack; |
| uptr beg, end, old_mid, new_mid; |
| |
| ErrorBadParamsToAnnotateContiguousContainer() = default; // (*) |
| // PS4: Do we want an AddressDescription for beg? |
| ErrorBadParamsToAnnotateContiguousContainer(u32 tid, |
| BufferedStackTrace *stack_, |
| uptr beg_, uptr end_, |
| uptr old_mid_, uptr new_mid_) |
| : ErrorBase(tid, 10, "bad-__sanitizer_annotate_contiguous_container"), |
| stack(stack_), |
| beg(beg_), |
| end(end_), |
| old_mid(old_mid_), |
| new_mid(new_mid_) {} |
| void Print(); |
| }; |
| |
| struct ErrorODRViolation : ErrorBase { |
| __asan_global global1, global2; |
| u32 stack_id1, stack_id2; |
| |
| ErrorODRViolation() = default; // (*) |
| ErrorODRViolation(u32 tid, const __asan_global *g1, u32 stack_id1_, |
| const __asan_global *g2, u32 stack_id2_) |
| : ErrorBase(tid, 10, "odr-violation"), |
| global1(*g1), |
| global2(*g2), |
| stack_id1(stack_id1_), |
| stack_id2(stack_id2_) {} |
| void Print(); |
| }; |
| |
| struct ErrorInvalidPointerPair : ErrorBase { |
| uptr pc, bp, sp; |
| AddressDescription addr1_description; |
| AddressDescription addr2_description; |
| |
| ErrorInvalidPointerPair() = default; // (*) |
| ErrorInvalidPointerPair(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr p1, |
| uptr p2) |
| : ErrorBase(tid, 10, "invalid-pointer-pair"), |
| pc(pc_), |
| bp(bp_), |
| sp(sp_), |
| addr1_description(p1, 1, /*shouldLockThreadRegistry=*/false), |
| addr2_description(p2, 1, /*shouldLockThreadRegistry=*/false) {} |
| void Print(); |
| }; |
| |
| struct ErrorGeneric : ErrorBase { |
| AddressDescription addr_description; |
| uptr pc, bp, sp; |
| uptr access_size; |
| const char *bug_descr; |
| bool is_write; |
| u8 shadow_val; |
| |
| ErrorGeneric() = default; // (*) |
| ErrorGeneric(u32 tid, uptr addr, uptr pc_, uptr bp_, uptr sp_, bool is_write_, |
| uptr access_size_); |
| void Print(); |
| }; |
| |
| // clang-format off |
| #define ASAN_FOR_EACH_ERROR_KIND(macro) \ |
| macro(DeadlySignal) \ |
| macro(DoubleFree) \ |
| macro(NewDeleteTypeMismatch) \ |
| macro(FreeNotMalloced) \ |
| macro(AllocTypeMismatch) \ |
| macro(MallocUsableSizeNotOwned) \ |
| macro(SanitizerGetAllocatedSizeNotOwned) \ |
| macro(CallocOverflow) \ |
| macro(ReallocArrayOverflow) \ |
| macro(PvallocOverflow) \ |
| macro(InvalidAllocationAlignment) \ |
| macro(InvalidAlignedAllocAlignment) \ |
| macro(InvalidPosixMemalignAlignment) \ |
| macro(AllocationSizeTooBig) \ |
| macro(RssLimitExceeded) \ |
| macro(OutOfMemory) \ |
| macro(StringFunctionMemoryRangesOverlap) \ |
| macro(StringFunctionSizeOverflow) \ |
| macro(BadParamsToAnnotateContiguousContainer) \ |
| macro(ODRViolation) \ |
| macro(InvalidPointerPair) \ |
| macro(Generic) |
| // clang-format on |
| |
| #define ASAN_DEFINE_ERROR_KIND(name) kErrorKind##name, |
| #define ASAN_ERROR_DESCRIPTION_MEMBER(name) Error##name name; |
| #define ASAN_ERROR_DESCRIPTION_CONSTRUCTOR(name) \ |
| ErrorDescription(Error##name const &e) : kind(kErrorKind##name) { \ |
| internal_memcpy(&name, &e, sizeof(name)); \ |
| } |
| #define ASAN_ERROR_DESCRIPTION_PRINT(name) \ |
| case kErrorKind##name: \ |
| return name.Print(); |
| |
| enum ErrorKind { |
| kErrorKindInvalid = 0, |
| ASAN_FOR_EACH_ERROR_KIND(ASAN_DEFINE_ERROR_KIND) |
| }; |
| |
| struct ErrorDescription { |
| ErrorKind kind; |
| // We're using a tagged union because it allows us to have a trivially |
| // copiable type and use the same structures as the public interface. |
| // |
| // We can add a wrapper around it to make it "more c++-like", but that would |
| // add a lot of code and the benefit wouldn't be that big. |
| union { |
| ErrorBase Base; |
| ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_MEMBER) |
| }; |
| |
| ErrorDescription() { internal_memset(this, 0, sizeof(*this)); } |
| explicit ErrorDescription(LinkerInitialized) {} |
| ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_CONSTRUCTOR) |
| |
| bool IsValid() { return kind != kErrorKindInvalid; } |
| void Print() { |
| switch (kind) { |
| ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_PRINT) |
| case kErrorKindInvalid: |
| CHECK(0); |
| } |
| CHECK(0); |
| } |
| }; |
| |
| #undef ASAN_FOR_EACH_ERROR_KIND |
| #undef ASAN_DEFINE_ERROR_KIND |
| #undef ASAN_ERROR_DESCRIPTION_MEMBER |
| #undef ASAN_ERROR_DESCRIPTION_CONSTRUCTOR |
| #undef ASAN_ERROR_DESCRIPTION_PRINT |
| |
| } // namespace __asan |
| |
| #endif // ASAN_ERRORS_H |