xref: /openbsd-src/gnu/llvm/compiler-rt/lib/scudo/standalone/report.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- report.cpp ----------------------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick 
93cab2bb3Spatrick #include "report.h"
103cab2bb3Spatrick 
113cab2bb3Spatrick #include "atomic_helpers.h"
123cab2bb3Spatrick #include "string_utils.h"
133cab2bb3Spatrick 
143cab2bb3Spatrick #include <stdarg.h>
153cab2bb3Spatrick 
163cab2bb3Spatrick namespace scudo {
173cab2bb3Spatrick 
183cab2bb3Spatrick class ScopedErrorReport {
193cab2bb3Spatrick public:
ScopedErrorReport()20d89ec533Spatrick   ScopedErrorReport() : Message() { Message.append("Scudo ERROR: "); }
append(const char * Format,...)213cab2bb3Spatrick   void append(const char *Format, ...) {
223cab2bb3Spatrick     va_list Args;
233cab2bb3Spatrick     va_start(Args, Format);
243cab2bb3Spatrick     Message.append(Format, Args);
253cab2bb3Spatrick     va_end(Args);
263cab2bb3Spatrick   }
~ScopedErrorReport()273cab2bb3Spatrick   NORETURN ~ScopedErrorReport() {
283cab2bb3Spatrick     outputRaw(Message.data());
293cab2bb3Spatrick     setAbortMessage(Message.data());
303cab2bb3Spatrick     die();
313cab2bb3Spatrick   }
323cab2bb3Spatrick 
333cab2bb3Spatrick private:
343cab2bb3Spatrick   ScopedString Message;
353cab2bb3Spatrick };
363cab2bb3Spatrick 
trap()373cab2bb3Spatrick inline void NORETURN trap() { __builtin_trap(); }
383cab2bb3Spatrick 
reportSoftRSSLimit(uptr RssLimitMb)39*810390e3Srobert void NORETURN reportSoftRSSLimit(uptr RssLimitMb) {
40*810390e3Srobert   ScopedErrorReport Report;
41*810390e3Srobert   Report.append("Soft RSS limit of %zu MB exhausted, current RSS is %zu MB\n",
42*810390e3Srobert                 RssLimitMb, GetRSS() >> 20);
43*810390e3Srobert }
44*810390e3Srobert 
reportHardRSSLimit(uptr RssLimitMb)45*810390e3Srobert void NORETURN reportHardRSSLimit(uptr RssLimitMb) {
46*810390e3Srobert   ScopedErrorReport Report;
47*810390e3Srobert   Report.append("Hard RSS limit of %zu MB exhausted, current RSS is %zu MB\n",
48*810390e3Srobert                 RssLimitMb, GetRSS() >> 20);
49*810390e3Srobert }
50*810390e3Srobert 
513cab2bb3Spatrick // 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)523cab2bb3Spatrick void NORETURN reportCheckFailed(const char *File, int Line,
533cab2bb3Spatrick                                 const char *Condition, u64 Value1, u64 Value2) {
543cab2bb3Spatrick   static atomic_u32 NumberOfCalls;
553cab2bb3Spatrick   if (atomic_fetch_add(&NumberOfCalls, 1, memory_order_relaxed) > 2) {
563cab2bb3Spatrick     // TODO(kostyak): maybe sleep here?
573cab2bb3Spatrick     trap();
583cab2bb3Spatrick   }
593cab2bb3Spatrick   ScopedErrorReport Report;
60d89ec533Spatrick   Report.append("CHECK failed @ %s:%d %s ((u64)op1=%llu, (u64)op2=%llu)\n",
61d89ec533Spatrick                 File, Line, Condition, Value1, Value2);
623cab2bb3Spatrick }
633cab2bb3Spatrick 
643cab2bb3Spatrick // Generic string fatal error message.
reportError(const char * Message)653cab2bb3Spatrick void NORETURN reportError(const char *Message) {
663cab2bb3Spatrick   ScopedErrorReport Report;
673cab2bb3Spatrick   Report.append("%s\n", Message);
683cab2bb3Spatrick }
693cab2bb3Spatrick 
reportInvalidFlag(const char * FlagType,const char * Value)703cab2bb3Spatrick void NORETURN reportInvalidFlag(const char *FlagType, const char *Value) {
713cab2bb3Spatrick   ScopedErrorReport Report;
723cab2bb3Spatrick   Report.append("invalid value for %s option: '%s'\n", FlagType, Value);
733cab2bb3Spatrick }
743cab2bb3Spatrick 
753cab2bb3Spatrick // The checksum of a chunk header is invalid. This could be caused by an
763cab2bb3Spatrick // {over,under}write of the header, a pointer that is not an actual chunk.
reportHeaderCorruption(void * Ptr)773cab2bb3Spatrick void NORETURN reportHeaderCorruption(void *Ptr) {
783cab2bb3Spatrick   ScopedErrorReport Report;
793cab2bb3Spatrick   Report.append("corrupted chunk header at address %p\n", Ptr);
803cab2bb3Spatrick }
813cab2bb3Spatrick 
823cab2bb3Spatrick // Two threads have attempted to modify a chunk header at the same time. This is
833cab2bb3Spatrick // symptomatic of a race-condition in the application code, or general lack of
843cab2bb3Spatrick // proper locking.
reportHeaderRace(void * Ptr)853cab2bb3Spatrick void NORETURN reportHeaderRace(void *Ptr) {
863cab2bb3Spatrick   ScopedErrorReport Report;
873cab2bb3Spatrick   Report.append("race on chunk header at address %p\n", Ptr);
883cab2bb3Spatrick }
893cab2bb3Spatrick 
903cab2bb3Spatrick // The allocator was compiled with parameters that conflict with field size
913cab2bb3Spatrick // requirements.
reportSanityCheckError(const char * Field)923cab2bb3Spatrick void NORETURN reportSanityCheckError(const char *Field) {
933cab2bb3Spatrick   ScopedErrorReport Report;
943cab2bb3Spatrick   Report.append("maximum possible %s doesn't fit in header\n", Field);
953cab2bb3Spatrick }
963cab2bb3Spatrick 
973cab2bb3Spatrick // We enforce a maximum alignment, to keep fields smaller and generally prevent
983cab2bb3Spatrick // integer overflows, or unexpected corner cases.
reportAlignmentTooBig(uptr Alignment,uptr MaxAlignment)993cab2bb3Spatrick void NORETURN reportAlignmentTooBig(uptr Alignment, uptr MaxAlignment) {
1003cab2bb3Spatrick   ScopedErrorReport Report;
1013cab2bb3Spatrick   Report.append("invalid allocation alignment: %zu exceeds maximum supported "
1023cab2bb3Spatrick                 "alignment of %zu\n",
1033cab2bb3Spatrick                 Alignment, MaxAlignment);
1043cab2bb3Spatrick }
1053cab2bb3Spatrick 
1063cab2bb3Spatrick // See above, we also enforce a maximum size.
reportAllocationSizeTooBig(uptr UserSize,uptr TotalSize,uptr MaxSize)1073cab2bb3Spatrick void NORETURN reportAllocationSizeTooBig(uptr UserSize, uptr TotalSize,
1083cab2bb3Spatrick                                          uptr MaxSize) {
1093cab2bb3Spatrick   ScopedErrorReport Report;
1103cab2bb3Spatrick   Report.append("requested allocation size %zu (%zu after adjustments) exceeds "
1113cab2bb3Spatrick                 "maximum supported size of %zu\n",
1123cab2bb3Spatrick                 UserSize, TotalSize, MaxSize);
1133cab2bb3Spatrick }
1143cab2bb3Spatrick 
reportOutOfMemory(uptr RequestedSize)1153cab2bb3Spatrick void NORETURN reportOutOfMemory(uptr RequestedSize) {
1163cab2bb3Spatrick   ScopedErrorReport Report;
1173cab2bb3Spatrick   Report.append("out of memory trying to allocate %zu bytes\n", RequestedSize);
1183cab2bb3Spatrick }
1193cab2bb3Spatrick 
stringifyAction(AllocatorAction Action)1203cab2bb3Spatrick static const char *stringifyAction(AllocatorAction Action) {
1213cab2bb3Spatrick   switch (Action) {
1223cab2bb3Spatrick   case AllocatorAction::Recycling:
1233cab2bb3Spatrick     return "recycling";
1243cab2bb3Spatrick   case AllocatorAction::Deallocating:
1253cab2bb3Spatrick     return "deallocating";
1263cab2bb3Spatrick   case AllocatorAction::Reallocating:
1273cab2bb3Spatrick     return "reallocating";
1283cab2bb3Spatrick   case AllocatorAction::Sizing:
1293cab2bb3Spatrick     return "sizing";
1303cab2bb3Spatrick   }
1313cab2bb3Spatrick   return "<invalid action>";
1323cab2bb3Spatrick }
1333cab2bb3Spatrick 
1343cab2bb3Spatrick // The chunk is not in a state congruent with the operation we want to perform.
1353cab2bb3Spatrick // This is usually the case with a double-free, a realloc of a freed pointer.
reportInvalidChunkState(AllocatorAction Action,void * Ptr)1363cab2bb3Spatrick void NORETURN reportInvalidChunkState(AllocatorAction Action, void *Ptr) {
1373cab2bb3Spatrick   ScopedErrorReport Report;
1383cab2bb3Spatrick   Report.append("invalid chunk state when %s address %p\n",
1393cab2bb3Spatrick                 stringifyAction(Action), Ptr);
1403cab2bb3Spatrick }
1413cab2bb3Spatrick 
reportMisalignedPointer(AllocatorAction Action,void * Ptr)1423cab2bb3Spatrick void NORETURN reportMisalignedPointer(AllocatorAction Action, void *Ptr) {
1433cab2bb3Spatrick   ScopedErrorReport Report;
1443cab2bb3Spatrick   Report.append("misaligned pointer when %s address %p\n",
1453cab2bb3Spatrick                 stringifyAction(Action), Ptr);
1463cab2bb3Spatrick }
1473cab2bb3Spatrick 
1483cab2bb3Spatrick // The deallocation function used is at odds with the one used to allocate the
1493cab2bb3Spatrick // chunk (eg: new[]/delete or malloc/delete, and so on).
reportDeallocTypeMismatch(AllocatorAction Action,void * Ptr,u8 TypeA,u8 TypeB)1503cab2bb3Spatrick void NORETURN reportDeallocTypeMismatch(AllocatorAction Action, void *Ptr,
1513cab2bb3Spatrick                                         u8 TypeA, u8 TypeB) {
1523cab2bb3Spatrick   ScopedErrorReport Report;
1533cab2bb3Spatrick   Report.append("allocation type mismatch when %s address %p (%d vs %d)\n",
1543cab2bb3Spatrick                 stringifyAction(Action), Ptr, TypeA, TypeB);
1553cab2bb3Spatrick }
1563cab2bb3Spatrick 
1573cab2bb3Spatrick // The size specified to the delete operator does not match the one that was
1583cab2bb3Spatrick // passed to new when allocating the chunk.
reportDeleteSizeMismatch(void * Ptr,uptr Size,uptr ExpectedSize)1593cab2bb3Spatrick void NORETURN reportDeleteSizeMismatch(void *Ptr, uptr Size,
1603cab2bb3Spatrick                                        uptr ExpectedSize) {
1613cab2bb3Spatrick   ScopedErrorReport Report;
1623cab2bb3Spatrick   Report.append(
1633cab2bb3Spatrick       "invalid sized delete when deallocating address %p (%zu vs %zu)\n", Ptr,
1643cab2bb3Spatrick       Size, ExpectedSize);
1653cab2bb3Spatrick }
1663cab2bb3Spatrick 
reportAlignmentNotPowerOfTwo(uptr Alignment)1673cab2bb3Spatrick void NORETURN reportAlignmentNotPowerOfTwo(uptr Alignment) {
1683cab2bb3Spatrick   ScopedErrorReport Report;
1693cab2bb3Spatrick   Report.append(
1703cab2bb3Spatrick       "invalid allocation alignment: %zu, alignment must be a power of two\n",
1713cab2bb3Spatrick       Alignment);
1723cab2bb3Spatrick }
1733cab2bb3Spatrick 
reportCallocOverflow(uptr Count,uptr Size)1743cab2bb3Spatrick void NORETURN reportCallocOverflow(uptr Count, uptr Size) {
1753cab2bb3Spatrick   ScopedErrorReport Report;
1763cab2bb3Spatrick   Report.append("calloc parameters overflow: count * size (%zu * %zu) cannot "
1773cab2bb3Spatrick                 "be represented with type size_t\n",
1783cab2bb3Spatrick                 Count, Size);
1793cab2bb3Spatrick }
1803cab2bb3Spatrick 
reportInvalidPosixMemalignAlignment(uptr Alignment)1813cab2bb3Spatrick void NORETURN reportInvalidPosixMemalignAlignment(uptr Alignment) {
1823cab2bb3Spatrick   ScopedErrorReport Report;
1833cab2bb3Spatrick   Report.append(
1843cab2bb3Spatrick       "invalid alignment requested in posix_memalign: %zu, alignment must be a "
1853cab2bb3Spatrick       "power of two and a multiple of sizeof(void *) == %zu\n",
1863cab2bb3Spatrick       Alignment, sizeof(void *));
1873cab2bb3Spatrick }
1883cab2bb3Spatrick 
reportPvallocOverflow(uptr Size)1893cab2bb3Spatrick void NORETURN reportPvallocOverflow(uptr Size) {
1903cab2bb3Spatrick   ScopedErrorReport Report;
1913cab2bb3Spatrick   Report.append("pvalloc parameters overflow: size %zu rounded up to system "
1923cab2bb3Spatrick                 "page size %zu cannot be represented in type size_t\n",
1933cab2bb3Spatrick                 Size, getPageSizeCached());
1943cab2bb3Spatrick }
1953cab2bb3Spatrick 
reportInvalidAlignedAllocAlignment(uptr Alignment,uptr Size)1963cab2bb3Spatrick void NORETURN reportInvalidAlignedAllocAlignment(uptr Alignment, uptr Size) {
1973cab2bb3Spatrick   ScopedErrorReport Report;
1983cab2bb3Spatrick   Report.append("invalid alignment requested in aligned_alloc: %zu, alignment "
1993cab2bb3Spatrick                 "must be a power of two and the requested size %zu must be a "
2003cab2bb3Spatrick                 "multiple of alignment\n",
2013cab2bb3Spatrick                 Alignment, Size);
2023cab2bb3Spatrick }
2033cab2bb3Spatrick 
2043cab2bb3Spatrick } // namespace scudo
205