//===-- 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" #include "sanitizer_common/sanitizer_symbolizer_amdgpu.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 ErrorFreeSizeMismatch : ErrorBase { const BufferedStackTrace* free_stack; HeapAddressDescription addr_description; uptr delete_size; uptr delete_alignment; ErrorFreeSizeMismatch() = default; // (*) ErrorFreeSizeMismatch(u32 tid, BufferedStackTrace* stack, uptr addr, uptr delete_size, uptr delete_alignment) : ErrorBase(tid, 10, "free-size-mismatch"), free_stack(stack), delete_size(delete_size), delete_alignment(delete_alignment) { GetHeapAddressInformation(addr, 1, &addr_description); } void Print(); bool isFreeAlignedSized() const { return delete_alignment != 0; } }; 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; bool is_write; ErrorStringFunctionSizeOverflow() = default; // (*) ErrorStringFunctionSizeOverflow(u32 tid, BufferedStackTrace* stack, uptr addr, uptr size, bool is_write) : ErrorBase(tid, 10, "negative-size-param"), stack(stack), addr_description(addr, /*shouldLockThreadRegistry=*/false), size(size), is_write(is_write) {} 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 ErrorBadParamsToAnnotateDoubleEndedContiguousContainer : ErrorBase { const BufferedStackTrace *stack; uptr storage_beg, storage_end, old_container_beg, old_container_end, new_container_beg, new_container_end; ErrorBadParamsToAnnotateDoubleEndedContiguousContainer() = default; // (*) ErrorBadParamsToAnnotateDoubleEndedContiguousContainer( u32 tid, BufferedStackTrace *stack_, uptr storage_beg_, uptr storage_end_, uptr old_container_beg_, uptr old_container_end_, uptr new_container_beg_, uptr new_container_end_) : ErrorBase(tid, 10, "bad-__sanitizer_annotate_double_ended_contiguous_container"), stack(stack_), storage_beg(storage_beg_), storage_end(storage_end_), old_container_beg(old_container_beg_), old_container_end(old_container_end_), new_container_beg(new_container_beg_), new_container_end(new_container_end_) {} 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 ErrorGenericBase : ErrorBase { AddressDescription addr_description; uptr access_size; bool is_write; u8 shadow_val; const char *bug_descr; ErrorGenericBase() = default; // (*) ErrorGenericBase(u32 tid, uptr addr_, bool is_write_, uptr access_size_); }; struct ErrorGeneric : ErrorGenericBase { uptr pc, bp, sp; ErrorGeneric() = default; // (*) ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr, bool is_write_, uptr access_size_); void Print(); }; // codeobject location for non-self error types. struct CodeObjectLocation { int fd; s64 vma_adjust; u64 offset, size; CodeObjectLocation() = default; CodeObjectLocation(int fd_, s64 vma_adjust_, u64 offset_, u64 size_) : fd(fd_), vma_adjust(vma_adjust_), offset(offset_), size(size_) {} }; // NonSelf Generic Error can be used to report // an error triggered by cpu thread that compiler-rt is not aware of struct ErrorNonSelfGeneric : ErrorGenericBase { CodeObjectLocation cb_loc; // At present, we assume one thread triggered the error static constexpr u32 threads_count = 1; static constexpr u32 addr_count = 1; static constexpr u32 maxcs_depth = 1; uptr addresses[addr_count]; u64 thread_id[threads_count]; uptr callstack[maxcs_depth]; ErrorNonSelfGeneric() = default; ErrorNonSelfGeneric(uptr *callstack_, u32 n_callstack, uptr *addrs, u32 n_addrs, u64 *threadids, u32 n_threads, bool is_write, u32 access_size, int fd_, s64 vm_adj, u64 off_, u64 sz_); void Print(); }; // AMDGPU Device Generic Error // Represents an invaid memory access made by a single amdgpu wave-front // Todo: abstract amdgpu related info into a base classes in case of // multiple error types for AMDGPU struct ErrorNonSelfAMDGPU : ErrorGenericBase { CodeObjectLocation cb_loc; // amdgpu wave-front can have atmost 64 active threads static constexpr u32 wavesize = 64; uptr device_address[wavesize]; // currently we don't support callstack of depth > 1 static constexpr u32 maxcs_depth = 1; uptr callstack[maxcs_depth]; struct workgroup_id { u64 idx, idy, idz; workgroup_id() = default; workgroup_id(u64 idx_, u64 idy_, u64 idz_) : idx(idx_), idy(idy_), idz(idz_) {} } wg; u64 workitem_ids[wavesize]; u32 nactive_threads; int device_id; ErrorNonSelfAMDGPU() = default; ErrorNonSelfAMDGPU(uptr *dev_callstack, u32 n_callstack, uptr *dev_address, u32 n_addrs, u64 *wi_ids, u32 n_wi, bool is_write_, u32 access_size_, int fd_, s64 vm_adj, u64 file_start_, u64 file_size_); void Print(); // error type identifying key static constexpr const char *key = "amdgpu"; private: void PrintStack(); void PrintThreadsAndAddresses(); void PrintMallocStack(); }; // clang-format off #define ASAN_FOR_EACH_ERROR_KIND(macro) \ macro(DeadlySignal) \ macro(DoubleFree) \ macro(NewDeleteTypeMismatch) \ macro(FreeSizeMismatch) \ 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(BadParamsToAnnotateDoubleEndedContiguousContainer) \ macro(ODRViolation) \ macro(InvalidPointerPair) \ macro(Generic) \ macro(NonSelfGeneric) \ macro(NonSelfAMDGPU) // 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