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: 20fe6060f1SDimitry Andric ScopedErrorReport() : Message() { Message.append("Scudo ERROR: "); } 2168d75effSDimitry Andric void append(const char *Format, ...) { 2268d75effSDimitry Andric va_list Args; 2368d75effSDimitry Andric va_start(Args, Format); 24*06c3fb27SDimitry Andric Message.vappend(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 37480093f4SDimitry Andric inline void NORETURN trap() { __builtin_trap(); } 3868d75effSDimitry Andric 39bdd1243dSDimitry Andric void NORETURN reportSoftRSSLimit(uptr RssLimitMb) { 40bdd1243dSDimitry Andric ScopedErrorReport Report; 41bdd1243dSDimitry Andric Report.append("Soft RSS limit of %zu MB exhausted, current RSS is %zu MB\n", 42bdd1243dSDimitry Andric RssLimitMb, GetRSS() >> 20); 43bdd1243dSDimitry Andric } 44bdd1243dSDimitry Andric 45bdd1243dSDimitry Andric void NORETURN reportHardRSSLimit(uptr RssLimitMb) { 46bdd1243dSDimitry Andric ScopedErrorReport Report; 47bdd1243dSDimitry Andric Report.append("Hard RSS limit of %zu MB exhausted, current RSS is %zu MB\n", 48bdd1243dSDimitry Andric RssLimitMb, GetRSS() >> 20); 49bdd1243dSDimitry Andric } 50bdd1243dSDimitry Andric 5168d75effSDimitry Andric // This could potentially be called recursively if a CHECK fails in the reports. 5268d75effSDimitry Andric void NORETURN reportCheckFailed(const char *File, int Line, 5368d75effSDimitry Andric const char *Condition, u64 Value1, u64 Value2) { 5468d75effSDimitry Andric static atomic_u32 NumberOfCalls; 5568d75effSDimitry Andric if (atomic_fetch_add(&NumberOfCalls, 1, memory_order_relaxed) > 2) { 5668d75effSDimitry Andric // TODO(kostyak): maybe sleep here? 5768d75effSDimitry Andric trap(); 5868d75effSDimitry Andric } 5968d75effSDimitry Andric ScopedErrorReport Report; 60fe6060f1SDimitry Andric Report.append("CHECK failed @ %s:%d %s ((u64)op1=%llu, (u64)op2=%llu)\n", 61fe6060f1SDimitry Andric File, Line, Condition, Value1, Value2); 6268d75effSDimitry Andric } 6368d75effSDimitry Andric 6468d75effSDimitry Andric // Generic string fatal error message. 6568d75effSDimitry Andric void NORETURN reportError(const char *Message) { 6668d75effSDimitry Andric ScopedErrorReport Report; 6768d75effSDimitry Andric Report.append("%s\n", Message); 6868d75effSDimitry Andric } 6968d75effSDimitry Andric 7068d75effSDimitry Andric void NORETURN reportInvalidFlag(const char *FlagType, const char *Value) { 7168d75effSDimitry Andric ScopedErrorReport Report; 7268d75effSDimitry Andric Report.append("invalid value for %s option: '%s'\n", FlagType, Value); 7368d75effSDimitry Andric } 7468d75effSDimitry Andric 7568d75effSDimitry Andric // The checksum of a chunk header is invalid. This could be caused by an 7668d75effSDimitry Andric // {over,under}write of the header, a pointer that is not an actual chunk. 7768d75effSDimitry Andric void NORETURN reportHeaderCorruption(void *Ptr) { 7868d75effSDimitry Andric ScopedErrorReport Report; 7968d75effSDimitry Andric Report.append("corrupted chunk header at address %p\n", Ptr); 8068d75effSDimitry Andric } 8168d75effSDimitry Andric 8268d75effSDimitry Andric // Two threads have attempted to modify a chunk header at the same time. This is 8368d75effSDimitry Andric // symptomatic of a race-condition in the application code, or general lack of 8468d75effSDimitry Andric // proper locking. 8568d75effSDimitry Andric void NORETURN reportHeaderRace(void *Ptr) { 8668d75effSDimitry Andric ScopedErrorReport Report; 8768d75effSDimitry Andric Report.append("race on chunk header at address %p\n", Ptr); 8868d75effSDimitry Andric } 8968d75effSDimitry Andric 9068d75effSDimitry Andric // The allocator was compiled with parameters that conflict with field size 9168d75effSDimitry Andric // requirements. 9268d75effSDimitry Andric void NORETURN reportSanityCheckError(const char *Field) { 9368d75effSDimitry Andric ScopedErrorReport Report; 9468d75effSDimitry Andric Report.append("maximum possible %s doesn't fit in header\n", Field); 9568d75effSDimitry Andric } 9668d75effSDimitry Andric 9768d75effSDimitry Andric // We enforce a maximum alignment, to keep fields smaller and generally prevent 9868d75effSDimitry Andric // integer overflows, or unexpected corner cases. 9968d75effSDimitry Andric void NORETURN reportAlignmentTooBig(uptr Alignment, uptr MaxAlignment) { 10068d75effSDimitry Andric ScopedErrorReport Report; 10168d75effSDimitry Andric Report.append("invalid allocation alignment: %zu exceeds maximum supported " 10268d75effSDimitry Andric "alignment of %zu\n", 10368d75effSDimitry Andric Alignment, MaxAlignment); 10468d75effSDimitry Andric } 10568d75effSDimitry Andric 10668d75effSDimitry Andric // See above, we also enforce a maximum size. 10768d75effSDimitry Andric void NORETURN reportAllocationSizeTooBig(uptr UserSize, uptr TotalSize, 10868d75effSDimitry Andric uptr MaxSize) { 10968d75effSDimitry Andric ScopedErrorReport Report; 11068d75effSDimitry Andric Report.append("requested allocation size %zu (%zu after adjustments) exceeds " 11168d75effSDimitry Andric "maximum supported size of %zu\n", 11268d75effSDimitry Andric UserSize, TotalSize, MaxSize); 11368d75effSDimitry Andric } 11468d75effSDimitry Andric 115*06c3fb27SDimitry Andric void NORETURN reportOutOfBatchClass() { 116*06c3fb27SDimitry Andric ScopedErrorReport Report; 117*06c3fb27SDimitry Andric Report.append("BatchClass region is used up, can't hold any free block\n"); 118*06c3fb27SDimitry Andric } 119*06c3fb27SDimitry Andric 12068d75effSDimitry Andric void NORETURN reportOutOfMemory(uptr RequestedSize) { 12168d75effSDimitry Andric ScopedErrorReport Report; 12268d75effSDimitry Andric Report.append("out of memory trying to allocate %zu bytes\n", RequestedSize); 12368d75effSDimitry Andric } 12468d75effSDimitry Andric 12568d75effSDimitry Andric static const char *stringifyAction(AllocatorAction Action) { 12668d75effSDimitry Andric switch (Action) { 12768d75effSDimitry Andric case AllocatorAction::Recycling: 12868d75effSDimitry Andric return "recycling"; 12968d75effSDimitry Andric case AllocatorAction::Deallocating: 13068d75effSDimitry Andric return "deallocating"; 13168d75effSDimitry Andric case AllocatorAction::Reallocating: 13268d75effSDimitry Andric return "reallocating"; 13368d75effSDimitry Andric case AllocatorAction::Sizing: 13468d75effSDimitry Andric return "sizing"; 13568d75effSDimitry Andric } 13668d75effSDimitry Andric return "<invalid action>"; 13768d75effSDimitry Andric } 13868d75effSDimitry Andric 13968d75effSDimitry Andric // The chunk is not in a state congruent with the operation we want to perform. 14068d75effSDimitry Andric // This is usually the case with a double-free, a realloc of a freed pointer. 14168d75effSDimitry Andric void NORETURN reportInvalidChunkState(AllocatorAction Action, void *Ptr) { 14268d75effSDimitry Andric ScopedErrorReport Report; 14368d75effSDimitry Andric Report.append("invalid chunk state when %s address %p\n", 14468d75effSDimitry Andric stringifyAction(Action), Ptr); 14568d75effSDimitry Andric } 14668d75effSDimitry Andric 14768d75effSDimitry Andric void NORETURN reportMisalignedPointer(AllocatorAction Action, void *Ptr) { 14868d75effSDimitry Andric ScopedErrorReport Report; 14968d75effSDimitry Andric Report.append("misaligned pointer when %s address %p\n", 15068d75effSDimitry Andric stringifyAction(Action), Ptr); 15168d75effSDimitry Andric } 15268d75effSDimitry Andric 15368d75effSDimitry Andric // The deallocation function used is at odds with the one used to allocate the 15468d75effSDimitry Andric // chunk (eg: new[]/delete or malloc/delete, and so on). 15568d75effSDimitry Andric void NORETURN reportDeallocTypeMismatch(AllocatorAction Action, void *Ptr, 15668d75effSDimitry Andric u8 TypeA, u8 TypeB) { 15768d75effSDimitry Andric ScopedErrorReport Report; 15868d75effSDimitry Andric Report.append("allocation type mismatch when %s address %p (%d vs %d)\n", 15968d75effSDimitry Andric stringifyAction(Action), Ptr, TypeA, TypeB); 16068d75effSDimitry Andric } 16168d75effSDimitry Andric 16268d75effSDimitry Andric // The size specified to the delete operator does not match the one that was 16368d75effSDimitry Andric // passed to new when allocating the chunk. 16468d75effSDimitry Andric void NORETURN reportDeleteSizeMismatch(void *Ptr, uptr Size, 16568d75effSDimitry Andric uptr ExpectedSize) { 16668d75effSDimitry Andric ScopedErrorReport Report; 16768d75effSDimitry Andric Report.append( 16868d75effSDimitry Andric "invalid sized delete when deallocating address %p (%zu vs %zu)\n", Ptr, 16968d75effSDimitry Andric Size, ExpectedSize); 17068d75effSDimitry Andric } 17168d75effSDimitry Andric 17268d75effSDimitry Andric void NORETURN reportAlignmentNotPowerOfTwo(uptr Alignment) { 17368d75effSDimitry Andric ScopedErrorReport Report; 17468d75effSDimitry Andric Report.append( 17568d75effSDimitry Andric "invalid allocation alignment: %zu, alignment must be a power of two\n", 17668d75effSDimitry Andric Alignment); 17768d75effSDimitry Andric } 17868d75effSDimitry Andric 17968d75effSDimitry Andric void NORETURN reportCallocOverflow(uptr Count, uptr Size) { 18068d75effSDimitry Andric ScopedErrorReport Report; 18168d75effSDimitry Andric Report.append("calloc parameters overflow: count * size (%zu * %zu) cannot " 18268d75effSDimitry Andric "be represented with type size_t\n", 18368d75effSDimitry Andric Count, Size); 18468d75effSDimitry Andric } 18568d75effSDimitry Andric 18668d75effSDimitry Andric void NORETURN reportInvalidPosixMemalignAlignment(uptr Alignment) { 18768d75effSDimitry Andric ScopedErrorReport Report; 18868d75effSDimitry Andric Report.append( 18968d75effSDimitry Andric "invalid alignment requested in posix_memalign: %zu, alignment must be a " 19068d75effSDimitry Andric "power of two and a multiple of sizeof(void *) == %zu\n", 19168d75effSDimitry Andric Alignment, sizeof(void *)); 19268d75effSDimitry Andric } 19368d75effSDimitry Andric 19468d75effSDimitry Andric void NORETURN reportPvallocOverflow(uptr Size) { 19568d75effSDimitry Andric ScopedErrorReport Report; 19668d75effSDimitry Andric Report.append("pvalloc parameters overflow: size %zu rounded up to system " 19768d75effSDimitry Andric "page size %zu cannot be represented in type size_t\n", 19868d75effSDimitry Andric Size, getPageSizeCached()); 19968d75effSDimitry Andric } 20068d75effSDimitry Andric 20168d75effSDimitry Andric void NORETURN reportInvalidAlignedAllocAlignment(uptr Alignment, uptr Size) { 20268d75effSDimitry Andric ScopedErrorReport Report; 20368d75effSDimitry Andric Report.append("invalid alignment requested in aligned_alloc: %zu, alignment " 20468d75effSDimitry Andric "must be a power of two and the requested size %zu must be a " 20568d75effSDimitry Andric "multiple of alignment\n", 20668d75effSDimitry Andric Alignment, Size); 20768d75effSDimitry Andric } 20868d75effSDimitry Andric 20968d75effSDimitry Andric } // namespace scudo 210