1*68d75effSDimitry Andric //===-- report.cpp ----------------------------------------------*- C++ -*-===// 2*68d75effSDimitry Andric // 3*68d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*68d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*68d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*68d75effSDimitry Andric // 7*68d75effSDimitry Andric //===----------------------------------------------------------------------===// 8*68d75effSDimitry Andric 9*68d75effSDimitry Andric #include "report.h" 10*68d75effSDimitry Andric 11*68d75effSDimitry Andric #include "atomic_helpers.h" 12*68d75effSDimitry Andric #include "string_utils.h" 13*68d75effSDimitry Andric 14*68d75effSDimitry Andric #include <stdarg.h> 15*68d75effSDimitry Andric 16*68d75effSDimitry Andric namespace scudo { 17*68d75effSDimitry Andric 18*68d75effSDimitry Andric class ScopedErrorReport { 19*68d75effSDimitry Andric public: 20*68d75effSDimitry Andric ScopedErrorReport() : Message(512) { Message.append("Scudo ERROR: "); } 21*68d75effSDimitry Andric void append(const char *Format, ...) { 22*68d75effSDimitry Andric va_list Args; 23*68d75effSDimitry Andric va_start(Args, Format); 24*68d75effSDimitry Andric Message.append(Format, Args); 25*68d75effSDimitry Andric va_end(Args); 26*68d75effSDimitry Andric } 27*68d75effSDimitry Andric NORETURN ~ScopedErrorReport() { 28*68d75effSDimitry Andric outputRaw(Message.data()); 29*68d75effSDimitry Andric setAbortMessage(Message.data()); 30*68d75effSDimitry Andric die(); 31*68d75effSDimitry Andric } 32*68d75effSDimitry Andric 33*68d75effSDimitry Andric private: 34*68d75effSDimitry Andric ScopedString Message; 35*68d75effSDimitry Andric }; 36*68d75effSDimitry Andric 37*68d75effSDimitry Andric INLINE void NORETURN trap() { __builtin_trap(); } 38*68d75effSDimitry Andric 39*68d75effSDimitry Andric // This could potentially be called recursively if a CHECK fails in the reports. 40*68d75effSDimitry Andric void NORETURN reportCheckFailed(const char *File, int Line, 41*68d75effSDimitry Andric const char *Condition, u64 Value1, u64 Value2) { 42*68d75effSDimitry Andric static atomic_u32 NumberOfCalls; 43*68d75effSDimitry Andric if (atomic_fetch_add(&NumberOfCalls, 1, memory_order_relaxed) > 2) { 44*68d75effSDimitry Andric // TODO(kostyak): maybe sleep here? 45*68d75effSDimitry Andric trap(); 46*68d75effSDimitry Andric } 47*68d75effSDimitry Andric ScopedErrorReport Report; 48*68d75effSDimitry Andric Report.append("CHECK failed @ %s:%d %s (%llu, %llu)\n", File, Line, Condition, 49*68d75effSDimitry Andric Value1, Value2); 50*68d75effSDimitry Andric } 51*68d75effSDimitry Andric 52*68d75effSDimitry Andric // Generic string fatal error message. 53*68d75effSDimitry Andric void NORETURN reportError(const char *Message) { 54*68d75effSDimitry Andric ScopedErrorReport Report; 55*68d75effSDimitry Andric Report.append("%s\n", Message); 56*68d75effSDimitry Andric } 57*68d75effSDimitry Andric 58*68d75effSDimitry Andric void NORETURN reportInvalidFlag(const char *FlagType, const char *Value) { 59*68d75effSDimitry Andric ScopedErrorReport Report; 60*68d75effSDimitry Andric Report.append("invalid value for %s option: '%s'\n", FlagType, Value); 61*68d75effSDimitry Andric } 62*68d75effSDimitry Andric 63*68d75effSDimitry Andric // The checksum of a chunk header is invalid. This could be caused by an 64*68d75effSDimitry Andric // {over,under}write of the header, a pointer that is not an actual chunk. 65*68d75effSDimitry Andric void NORETURN reportHeaderCorruption(void *Ptr) { 66*68d75effSDimitry Andric ScopedErrorReport Report; 67*68d75effSDimitry Andric Report.append("corrupted chunk header at address %p\n", Ptr); 68*68d75effSDimitry Andric } 69*68d75effSDimitry Andric 70*68d75effSDimitry Andric // Two threads have attempted to modify a chunk header at the same time. This is 71*68d75effSDimitry Andric // symptomatic of a race-condition in the application code, or general lack of 72*68d75effSDimitry Andric // proper locking. 73*68d75effSDimitry Andric void NORETURN reportHeaderRace(void *Ptr) { 74*68d75effSDimitry Andric ScopedErrorReport Report; 75*68d75effSDimitry Andric Report.append("race on chunk header at address %p\n", Ptr); 76*68d75effSDimitry Andric } 77*68d75effSDimitry Andric 78*68d75effSDimitry Andric // The allocator was compiled with parameters that conflict with field size 79*68d75effSDimitry Andric // requirements. 80*68d75effSDimitry Andric void NORETURN reportSanityCheckError(const char *Field) { 81*68d75effSDimitry Andric ScopedErrorReport Report; 82*68d75effSDimitry Andric Report.append("maximum possible %s doesn't fit in header\n", Field); 83*68d75effSDimitry Andric } 84*68d75effSDimitry Andric 85*68d75effSDimitry Andric // We enforce a maximum alignment, to keep fields smaller and generally prevent 86*68d75effSDimitry Andric // integer overflows, or unexpected corner cases. 87*68d75effSDimitry Andric void NORETURN reportAlignmentTooBig(uptr Alignment, uptr MaxAlignment) { 88*68d75effSDimitry Andric ScopedErrorReport Report; 89*68d75effSDimitry Andric Report.append("invalid allocation alignment: %zu exceeds maximum supported " 90*68d75effSDimitry Andric "alignment of %zu\n", 91*68d75effSDimitry Andric Alignment, MaxAlignment); 92*68d75effSDimitry Andric } 93*68d75effSDimitry Andric 94*68d75effSDimitry Andric // See above, we also enforce a maximum size. 95*68d75effSDimitry Andric void NORETURN reportAllocationSizeTooBig(uptr UserSize, uptr TotalSize, 96*68d75effSDimitry Andric uptr MaxSize) { 97*68d75effSDimitry Andric ScopedErrorReport Report; 98*68d75effSDimitry Andric Report.append("requested allocation size %zu (%zu after adjustments) exceeds " 99*68d75effSDimitry Andric "maximum supported size of %zu\n", 100*68d75effSDimitry Andric UserSize, TotalSize, MaxSize); 101*68d75effSDimitry Andric } 102*68d75effSDimitry Andric 103*68d75effSDimitry Andric void NORETURN reportOutOfMemory(uptr RequestedSize) { 104*68d75effSDimitry Andric ScopedErrorReport Report; 105*68d75effSDimitry Andric Report.append("out of memory trying to allocate %zu bytes\n", RequestedSize); 106*68d75effSDimitry Andric } 107*68d75effSDimitry Andric 108*68d75effSDimitry Andric static const char *stringifyAction(AllocatorAction Action) { 109*68d75effSDimitry Andric switch (Action) { 110*68d75effSDimitry Andric case AllocatorAction::Recycling: 111*68d75effSDimitry Andric return "recycling"; 112*68d75effSDimitry Andric case AllocatorAction::Deallocating: 113*68d75effSDimitry Andric return "deallocating"; 114*68d75effSDimitry Andric case AllocatorAction::Reallocating: 115*68d75effSDimitry Andric return "reallocating"; 116*68d75effSDimitry Andric case AllocatorAction::Sizing: 117*68d75effSDimitry Andric return "sizing"; 118*68d75effSDimitry Andric } 119*68d75effSDimitry Andric return "<invalid action>"; 120*68d75effSDimitry Andric } 121*68d75effSDimitry Andric 122*68d75effSDimitry Andric // The chunk is not in a state congruent with the operation we want to perform. 123*68d75effSDimitry Andric // This is usually the case with a double-free, a realloc of a freed pointer. 124*68d75effSDimitry Andric void NORETURN reportInvalidChunkState(AllocatorAction Action, void *Ptr) { 125*68d75effSDimitry Andric ScopedErrorReport Report; 126*68d75effSDimitry Andric Report.append("invalid chunk state when %s address %p\n", 127*68d75effSDimitry Andric stringifyAction(Action), Ptr); 128*68d75effSDimitry Andric } 129*68d75effSDimitry Andric 130*68d75effSDimitry Andric void NORETURN reportMisalignedPointer(AllocatorAction Action, void *Ptr) { 131*68d75effSDimitry Andric ScopedErrorReport Report; 132*68d75effSDimitry Andric Report.append("misaligned pointer when %s address %p\n", 133*68d75effSDimitry Andric stringifyAction(Action), Ptr); 134*68d75effSDimitry Andric } 135*68d75effSDimitry Andric 136*68d75effSDimitry Andric // The deallocation function used is at odds with the one used to allocate the 137*68d75effSDimitry Andric // chunk (eg: new[]/delete or malloc/delete, and so on). 138*68d75effSDimitry Andric void NORETURN reportDeallocTypeMismatch(AllocatorAction Action, void *Ptr, 139*68d75effSDimitry Andric u8 TypeA, u8 TypeB) { 140*68d75effSDimitry Andric ScopedErrorReport Report; 141*68d75effSDimitry Andric Report.append("allocation type mismatch when %s address %p (%d vs %d)\n", 142*68d75effSDimitry Andric stringifyAction(Action), Ptr, TypeA, TypeB); 143*68d75effSDimitry Andric } 144*68d75effSDimitry Andric 145*68d75effSDimitry Andric // The size specified to the delete operator does not match the one that was 146*68d75effSDimitry Andric // passed to new when allocating the chunk. 147*68d75effSDimitry Andric void NORETURN reportDeleteSizeMismatch(void *Ptr, uptr Size, 148*68d75effSDimitry Andric uptr ExpectedSize) { 149*68d75effSDimitry Andric ScopedErrorReport Report; 150*68d75effSDimitry Andric Report.append( 151*68d75effSDimitry Andric "invalid sized delete when deallocating address %p (%zu vs %zu)\n", Ptr, 152*68d75effSDimitry Andric Size, ExpectedSize); 153*68d75effSDimitry Andric } 154*68d75effSDimitry Andric 155*68d75effSDimitry Andric void NORETURN reportAlignmentNotPowerOfTwo(uptr Alignment) { 156*68d75effSDimitry Andric ScopedErrorReport Report; 157*68d75effSDimitry Andric Report.append( 158*68d75effSDimitry Andric "invalid allocation alignment: %zu, alignment must be a power of two\n", 159*68d75effSDimitry Andric Alignment); 160*68d75effSDimitry Andric } 161*68d75effSDimitry Andric 162*68d75effSDimitry Andric void NORETURN reportCallocOverflow(uptr Count, uptr Size) { 163*68d75effSDimitry Andric ScopedErrorReport Report; 164*68d75effSDimitry Andric Report.append("calloc parameters overflow: count * size (%zu * %zu) cannot " 165*68d75effSDimitry Andric "be represented with type size_t\n", 166*68d75effSDimitry Andric Count, Size); 167*68d75effSDimitry Andric } 168*68d75effSDimitry Andric 169*68d75effSDimitry Andric void NORETURN reportInvalidPosixMemalignAlignment(uptr Alignment) { 170*68d75effSDimitry Andric ScopedErrorReport Report; 171*68d75effSDimitry Andric Report.append( 172*68d75effSDimitry Andric "invalid alignment requested in posix_memalign: %zu, alignment must be a " 173*68d75effSDimitry Andric "power of two and a multiple of sizeof(void *) == %zu\n", 174*68d75effSDimitry Andric Alignment, sizeof(void *)); 175*68d75effSDimitry Andric } 176*68d75effSDimitry Andric 177*68d75effSDimitry Andric void NORETURN reportPvallocOverflow(uptr Size) { 178*68d75effSDimitry Andric ScopedErrorReport Report; 179*68d75effSDimitry Andric Report.append("pvalloc parameters overflow: size %zu rounded up to system " 180*68d75effSDimitry Andric "page size %zu cannot be represented in type size_t\n", 181*68d75effSDimitry Andric Size, getPageSizeCached()); 182*68d75effSDimitry Andric } 183*68d75effSDimitry Andric 184*68d75effSDimitry Andric void NORETURN reportInvalidAlignedAllocAlignment(uptr Alignment, uptr Size) { 185*68d75effSDimitry Andric ScopedErrorReport Report; 186*68d75effSDimitry Andric Report.append("invalid alignment requested in aligned_alloc: %zu, alignment " 187*68d75effSDimitry Andric "must be a power of two and the requested size %zu must be a " 188*68d75effSDimitry Andric "multiple of alignment\n", 189*68d75effSDimitry Andric Alignment, Size); 190*68d75effSDimitry Andric } 191*68d75effSDimitry Andric 192*68d75effSDimitry Andric } // namespace scudo 193