xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/scudo/standalone/report.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
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