168d75effSDimitry Andric //===-- report.cpp ----------------------------------------------*- C++ -*-===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric 968d75effSDimitry Andric #include "report.h" 1068d75effSDimitry Andric 1168d75effSDimitry Andric #include "atomic_helpers.h" 1268d75effSDimitry Andric #include "string_utils.h" 1368d75effSDimitry Andric 1468d75effSDimitry Andric #include <stdarg.h> 1568d75effSDimitry Andric 1668d75effSDimitry Andric namespace scudo { 1768d75effSDimitry Andric 1868d75effSDimitry Andric class ScopedErrorReport { 1968d75effSDimitry Andric public: 2068d75effSDimitry Andric ScopedErrorReport() : Message(512) { Message.append("Scudo ERROR: "); } 2168d75effSDimitry Andric void append(const char *Format, ...) { 2268d75effSDimitry Andric va_list Args; 2368d75effSDimitry Andric va_start(Args, Format); 2468d75effSDimitry Andric Message.append(Format, Args); 2568d75effSDimitry Andric va_end(Args); 2668d75effSDimitry Andric } 2768d75effSDimitry Andric NORETURN ~ScopedErrorReport() { 2868d75effSDimitry Andric outputRaw(Message.data()); 2968d75effSDimitry Andric setAbortMessage(Message.data()); 3068d75effSDimitry Andric die(); 3168d75effSDimitry Andric } 3268d75effSDimitry Andric 3368d75effSDimitry Andric private: 3468d75effSDimitry Andric ScopedString Message; 3568d75effSDimitry Andric }; 3668d75effSDimitry Andric 37*480093f4SDimitry Andric inline void NORETURN trap() { __builtin_trap(); } 3868d75effSDimitry Andric 3968d75effSDimitry Andric // This could potentially be called recursively if a CHECK fails in the reports. 4068d75effSDimitry Andric void NORETURN reportCheckFailed(const char *File, int Line, 4168d75effSDimitry Andric const char *Condition, u64 Value1, u64 Value2) { 4268d75effSDimitry Andric static atomic_u32 NumberOfCalls; 4368d75effSDimitry Andric if (atomic_fetch_add(&NumberOfCalls, 1, memory_order_relaxed) > 2) { 4468d75effSDimitry Andric // TODO(kostyak): maybe sleep here? 4568d75effSDimitry Andric trap(); 4668d75effSDimitry Andric } 4768d75effSDimitry Andric ScopedErrorReport Report; 4868d75effSDimitry Andric Report.append("CHECK failed @ %s:%d %s (%llu, %llu)\n", File, Line, Condition, 4968d75effSDimitry Andric Value1, Value2); 5068d75effSDimitry Andric } 5168d75effSDimitry Andric 5268d75effSDimitry Andric // Generic string fatal error message. 5368d75effSDimitry Andric void NORETURN reportError(const char *Message) { 5468d75effSDimitry Andric ScopedErrorReport Report; 5568d75effSDimitry Andric Report.append("%s\n", Message); 5668d75effSDimitry Andric } 5768d75effSDimitry Andric 5868d75effSDimitry Andric void NORETURN reportInvalidFlag(const char *FlagType, const char *Value) { 5968d75effSDimitry Andric ScopedErrorReport Report; 6068d75effSDimitry Andric Report.append("invalid value for %s option: '%s'\n", FlagType, Value); 6168d75effSDimitry Andric } 6268d75effSDimitry Andric 6368d75effSDimitry Andric // The checksum of a chunk header is invalid. This could be caused by an 6468d75effSDimitry Andric // {over,under}write of the header, a pointer that is not an actual chunk. 6568d75effSDimitry Andric void NORETURN reportHeaderCorruption(void *Ptr) { 6668d75effSDimitry Andric ScopedErrorReport Report; 6768d75effSDimitry Andric Report.append("corrupted chunk header at address %p\n", Ptr); 6868d75effSDimitry Andric } 6968d75effSDimitry Andric 7068d75effSDimitry Andric // Two threads have attempted to modify a chunk header at the same time. This is 7168d75effSDimitry Andric // symptomatic of a race-condition in the application code, or general lack of 7268d75effSDimitry Andric // proper locking. 7368d75effSDimitry Andric void NORETURN reportHeaderRace(void *Ptr) { 7468d75effSDimitry Andric ScopedErrorReport Report; 7568d75effSDimitry Andric Report.append("race on chunk header at address %p\n", Ptr); 7668d75effSDimitry Andric } 7768d75effSDimitry Andric 7868d75effSDimitry Andric // The allocator was compiled with parameters that conflict with field size 7968d75effSDimitry Andric // requirements. 8068d75effSDimitry Andric void NORETURN reportSanityCheckError(const char *Field) { 8168d75effSDimitry Andric ScopedErrorReport Report; 8268d75effSDimitry Andric Report.append("maximum possible %s doesn't fit in header\n", Field); 8368d75effSDimitry Andric } 8468d75effSDimitry Andric 8568d75effSDimitry Andric // We enforce a maximum alignment, to keep fields smaller and generally prevent 8668d75effSDimitry Andric // integer overflows, or unexpected corner cases. 8768d75effSDimitry Andric void NORETURN reportAlignmentTooBig(uptr Alignment, uptr MaxAlignment) { 8868d75effSDimitry Andric ScopedErrorReport Report; 8968d75effSDimitry Andric Report.append("invalid allocation alignment: %zu exceeds maximum supported " 9068d75effSDimitry Andric "alignment of %zu\n", 9168d75effSDimitry Andric Alignment, MaxAlignment); 9268d75effSDimitry Andric } 9368d75effSDimitry Andric 9468d75effSDimitry Andric // See above, we also enforce a maximum size. 9568d75effSDimitry Andric void NORETURN reportAllocationSizeTooBig(uptr UserSize, uptr TotalSize, 9668d75effSDimitry Andric uptr MaxSize) { 9768d75effSDimitry Andric ScopedErrorReport Report; 9868d75effSDimitry Andric Report.append("requested allocation size %zu (%zu after adjustments) exceeds " 9968d75effSDimitry Andric "maximum supported size of %zu\n", 10068d75effSDimitry Andric UserSize, TotalSize, MaxSize); 10168d75effSDimitry Andric } 10268d75effSDimitry Andric 10368d75effSDimitry Andric void NORETURN reportOutOfMemory(uptr RequestedSize) { 10468d75effSDimitry Andric ScopedErrorReport Report; 10568d75effSDimitry Andric Report.append("out of memory trying to allocate %zu bytes\n", RequestedSize); 10668d75effSDimitry Andric } 10768d75effSDimitry Andric 10868d75effSDimitry Andric static const char *stringifyAction(AllocatorAction Action) { 10968d75effSDimitry Andric switch (Action) { 11068d75effSDimitry Andric case AllocatorAction::Recycling: 11168d75effSDimitry Andric return "recycling"; 11268d75effSDimitry Andric case AllocatorAction::Deallocating: 11368d75effSDimitry Andric return "deallocating"; 11468d75effSDimitry Andric case AllocatorAction::Reallocating: 11568d75effSDimitry Andric return "reallocating"; 11668d75effSDimitry Andric case AllocatorAction::Sizing: 11768d75effSDimitry Andric return "sizing"; 11868d75effSDimitry Andric } 11968d75effSDimitry Andric return "<invalid action>"; 12068d75effSDimitry Andric } 12168d75effSDimitry Andric 12268d75effSDimitry Andric // The chunk is not in a state congruent with the operation we want to perform. 12368d75effSDimitry Andric // This is usually the case with a double-free, a realloc of a freed pointer. 12468d75effSDimitry Andric void NORETURN reportInvalidChunkState(AllocatorAction Action, void *Ptr) { 12568d75effSDimitry Andric ScopedErrorReport Report; 12668d75effSDimitry Andric Report.append("invalid chunk state when %s address %p\n", 12768d75effSDimitry Andric stringifyAction(Action), Ptr); 12868d75effSDimitry Andric } 12968d75effSDimitry Andric 13068d75effSDimitry Andric void NORETURN reportMisalignedPointer(AllocatorAction Action, void *Ptr) { 13168d75effSDimitry Andric ScopedErrorReport Report; 13268d75effSDimitry Andric Report.append("misaligned pointer when %s address %p\n", 13368d75effSDimitry Andric stringifyAction(Action), Ptr); 13468d75effSDimitry Andric } 13568d75effSDimitry Andric 13668d75effSDimitry Andric // The deallocation function used is at odds with the one used to allocate the 13768d75effSDimitry Andric // chunk (eg: new[]/delete or malloc/delete, and so on). 13868d75effSDimitry Andric void NORETURN reportDeallocTypeMismatch(AllocatorAction Action, void *Ptr, 13968d75effSDimitry Andric u8 TypeA, u8 TypeB) { 14068d75effSDimitry Andric ScopedErrorReport Report; 14168d75effSDimitry Andric Report.append("allocation type mismatch when %s address %p (%d vs %d)\n", 14268d75effSDimitry Andric stringifyAction(Action), Ptr, TypeA, TypeB); 14368d75effSDimitry Andric } 14468d75effSDimitry Andric 14568d75effSDimitry Andric // The size specified to the delete operator does not match the one that was 14668d75effSDimitry Andric // passed to new when allocating the chunk. 14768d75effSDimitry Andric void NORETURN reportDeleteSizeMismatch(void *Ptr, uptr Size, 14868d75effSDimitry Andric uptr ExpectedSize) { 14968d75effSDimitry Andric ScopedErrorReport Report; 15068d75effSDimitry Andric Report.append( 15168d75effSDimitry Andric "invalid sized delete when deallocating address %p (%zu vs %zu)\n", Ptr, 15268d75effSDimitry Andric Size, ExpectedSize); 15368d75effSDimitry Andric } 15468d75effSDimitry Andric 15568d75effSDimitry Andric void NORETURN reportAlignmentNotPowerOfTwo(uptr Alignment) { 15668d75effSDimitry Andric ScopedErrorReport Report; 15768d75effSDimitry Andric Report.append( 15868d75effSDimitry Andric "invalid allocation alignment: %zu, alignment must be a power of two\n", 15968d75effSDimitry Andric Alignment); 16068d75effSDimitry Andric } 16168d75effSDimitry Andric 16268d75effSDimitry Andric void NORETURN reportCallocOverflow(uptr Count, uptr Size) { 16368d75effSDimitry Andric ScopedErrorReport Report; 16468d75effSDimitry Andric Report.append("calloc parameters overflow: count * size (%zu * %zu) cannot " 16568d75effSDimitry Andric "be represented with type size_t\n", 16668d75effSDimitry Andric Count, Size); 16768d75effSDimitry Andric } 16868d75effSDimitry Andric 16968d75effSDimitry Andric void NORETURN reportInvalidPosixMemalignAlignment(uptr Alignment) { 17068d75effSDimitry Andric ScopedErrorReport Report; 17168d75effSDimitry Andric Report.append( 17268d75effSDimitry Andric "invalid alignment requested in posix_memalign: %zu, alignment must be a " 17368d75effSDimitry Andric "power of two and a multiple of sizeof(void *) == %zu\n", 17468d75effSDimitry Andric Alignment, sizeof(void *)); 17568d75effSDimitry Andric } 17668d75effSDimitry Andric 17768d75effSDimitry Andric void NORETURN reportPvallocOverflow(uptr Size) { 17868d75effSDimitry Andric ScopedErrorReport Report; 17968d75effSDimitry Andric Report.append("pvalloc parameters overflow: size %zu rounded up to system " 18068d75effSDimitry Andric "page size %zu cannot be represented in type size_t\n", 18168d75effSDimitry Andric Size, getPageSizeCached()); 18268d75effSDimitry Andric } 18368d75effSDimitry Andric 18468d75effSDimitry Andric void NORETURN reportInvalidAlignedAllocAlignment(uptr Alignment, uptr Size) { 18568d75effSDimitry Andric ScopedErrorReport Report; 18668d75effSDimitry Andric Report.append("invalid alignment requested in aligned_alloc: %zu, alignment " 18768d75effSDimitry Andric "must be a power of two and the requested size %zu must be a " 18868d75effSDimitry Andric "multiple of alignment\n", 18968d75effSDimitry Andric Alignment, Size); 19068d75effSDimitry Andric } 19168d75effSDimitry Andric 19268d75effSDimitry Andric } // namespace scudo 193