Updated: Removed offending TODO comment. Dereferences with addresses above the 48-bit hardware addressable range produce "invalid instruction" (instead of "invalid access") hardware exceptions (there is no hardware address decoding logic for those bits), and the address provided by this exception is the address of the instruction (not the faulting address). The kernel maps the "invalid instruction" to SEGV, but fails to provide the real fault address. Because of this ASan lies and says that those cases are null dereferences. This downgrades the severity of a found bug in terms of security. In the ASan signal handler, we can not provide the real faulting address, but at least we can try not to lie. rdar://50366151 Reviewed By: vitalybuka Differential Revision: https://reviews.llvm.org/D68676 > llvm-svn: 374265 llvm-svn: 374384
457 lines
15 KiB
C++
457 lines
15 KiB
C++
//===-- 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
|