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