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:
ScopedErrorReport()20fe6060f1SDimitry Andric   ScopedErrorReport() : Message() { Message.append("Scudo ERROR: "); }
append(const char * Format,...)2168d75effSDimitry Andric   void append(const char *Format, ...) {
2268d75effSDimitry Andric     va_list Args;
2368d75effSDimitry Andric     va_start(Args, Format);
2406c3fb27SDimitry Andric     Message.vappend(Format, Args);
2568d75effSDimitry Andric     va_end(Args);
2668d75effSDimitry Andric   }
~ScopedErrorReport()27*5f757f3fSDimitry Andric   NORETURN ~ScopedErrorReport() { reportRawError(Message.data()); }
2868d75effSDimitry Andric 
2968d75effSDimitry Andric private:
3068d75effSDimitry Andric   ScopedString Message;
3168d75effSDimitry Andric };
3268d75effSDimitry Andric 
trap()33480093f4SDimitry Andric inline void NORETURN trap() { __builtin_trap(); }
3468d75effSDimitry Andric 
3568d75effSDimitry Andric // This could potentially be called recursively if a CHECK fails in the reports.
reportCheckFailed(const char * File,int Line,const char * Condition,u64 Value1,u64 Value2)3668d75effSDimitry Andric void NORETURN reportCheckFailed(const char *File, int Line,
3768d75effSDimitry Andric                                 const char *Condition, u64 Value1, u64 Value2) {
3868d75effSDimitry Andric   static atomic_u32 NumberOfCalls;
3968d75effSDimitry Andric   if (atomic_fetch_add(&NumberOfCalls, 1, memory_order_relaxed) > 2) {
4068d75effSDimitry Andric     // TODO(kostyak): maybe sleep here?
4168d75effSDimitry Andric     trap();
4268d75effSDimitry Andric   }
4368d75effSDimitry Andric   ScopedErrorReport Report;
44fe6060f1SDimitry Andric   Report.append("CHECK failed @ %s:%d %s ((u64)op1=%llu, (u64)op2=%llu)\n",
45fe6060f1SDimitry Andric                 File, Line, Condition, Value1, Value2);
4668d75effSDimitry Andric }
4768d75effSDimitry Andric 
4868d75effSDimitry Andric // Generic string fatal error message.
reportError(const char * Message)4968d75effSDimitry Andric void NORETURN reportError(const char *Message) {
5068d75effSDimitry Andric   ScopedErrorReport Report;
5168d75effSDimitry Andric   Report.append("%s\n", Message);
5268d75effSDimitry Andric }
5368d75effSDimitry Andric 
54*5f757f3fSDimitry Andric // Generic fatal error message without ScopedString.
reportRawError(const char * Message)55*5f757f3fSDimitry Andric void NORETURN reportRawError(const char *Message) {
56*5f757f3fSDimitry Andric   outputRaw(Message);
57*5f757f3fSDimitry Andric   setAbortMessage(Message);
58*5f757f3fSDimitry Andric   die();
59*5f757f3fSDimitry Andric }
60*5f757f3fSDimitry Andric 
reportInvalidFlag(const char * FlagType,const char * Value)6168d75effSDimitry Andric void NORETURN reportInvalidFlag(const char *FlagType, const char *Value) {
6268d75effSDimitry Andric   ScopedErrorReport Report;
6368d75effSDimitry Andric   Report.append("invalid value for %s option: '%s'\n", FlagType, Value);
6468d75effSDimitry Andric }
6568d75effSDimitry Andric 
6668d75effSDimitry Andric // The checksum of a chunk header is invalid. This could be caused by an
6768d75effSDimitry Andric // {over,under}write of the header, a pointer that is not an actual chunk.
reportHeaderCorruption(void * Ptr)6868d75effSDimitry Andric void NORETURN reportHeaderCorruption(void *Ptr) {
6968d75effSDimitry Andric   ScopedErrorReport Report;
7068d75effSDimitry Andric   Report.append("corrupted chunk header at address %p\n", Ptr);
7168d75effSDimitry Andric }
7268d75effSDimitry Andric 
7368d75effSDimitry Andric // The allocator was compiled with parameters that conflict with field size
7468d75effSDimitry Andric // requirements.
reportSanityCheckError(const char * Field)7568d75effSDimitry Andric void NORETURN reportSanityCheckError(const char *Field) {
7668d75effSDimitry Andric   ScopedErrorReport Report;
7768d75effSDimitry Andric   Report.append("maximum possible %s doesn't fit in header\n", Field);
7868d75effSDimitry Andric }
7968d75effSDimitry Andric 
8068d75effSDimitry Andric // We enforce a maximum alignment, to keep fields smaller and generally prevent
8168d75effSDimitry Andric // integer overflows, or unexpected corner cases.
reportAlignmentTooBig(uptr Alignment,uptr MaxAlignment)8268d75effSDimitry Andric void NORETURN reportAlignmentTooBig(uptr Alignment, uptr MaxAlignment) {
8368d75effSDimitry Andric   ScopedErrorReport Report;
8468d75effSDimitry Andric   Report.append("invalid allocation alignment: %zu exceeds maximum supported "
8568d75effSDimitry Andric                 "alignment of %zu\n",
8668d75effSDimitry Andric                 Alignment, MaxAlignment);
8768d75effSDimitry Andric }
8868d75effSDimitry Andric 
8968d75effSDimitry Andric // See above, we also enforce a maximum size.
reportAllocationSizeTooBig(uptr UserSize,uptr TotalSize,uptr MaxSize)9068d75effSDimitry Andric void NORETURN reportAllocationSizeTooBig(uptr UserSize, uptr TotalSize,
9168d75effSDimitry Andric                                          uptr MaxSize) {
9268d75effSDimitry Andric   ScopedErrorReport Report;
9368d75effSDimitry Andric   Report.append("requested allocation size %zu (%zu after adjustments) exceeds "
9468d75effSDimitry Andric                 "maximum supported size of %zu\n",
9568d75effSDimitry Andric                 UserSize, TotalSize, MaxSize);
9668d75effSDimitry Andric }
9768d75effSDimitry Andric 
reportOutOfBatchClass()9806c3fb27SDimitry Andric void NORETURN reportOutOfBatchClass() {
9906c3fb27SDimitry Andric   ScopedErrorReport Report;
10006c3fb27SDimitry Andric   Report.append("BatchClass region is used up, can't hold any free block\n");
10106c3fb27SDimitry Andric }
10206c3fb27SDimitry Andric 
reportOutOfMemory(uptr RequestedSize)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 
stringifyAction(AllocatorAction Action)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.
reportInvalidChunkState(AllocatorAction Action,void * Ptr)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 
reportMisalignedPointer(AllocatorAction Action,void * Ptr)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).
reportDeallocTypeMismatch(AllocatorAction Action,void * Ptr,u8 TypeA,u8 TypeB)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.
reportDeleteSizeMismatch(void * Ptr,uptr Size,uptr ExpectedSize)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 
reportAlignmentNotPowerOfTwo(uptr Alignment)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 
reportCallocOverflow(uptr Count,uptr Size)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 
reportInvalidPosixMemalignAlignment(uptr Alignment)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 
reportPvallocOverflow(uptr Size)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 
reportInvalidAlignedAllocAlignment(uptr Alignment,uptr Size)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