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