xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/scudo/standalone/report.cpp (revision 480093f4440d54b30b3025afeac24b48f2ba7a2e)
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:
2068d75effSDimitry Andric   ScopedErrorReport() : Message(512) { Message.append("Scudo ERROR: "); }
2168d75effSDimitry Andric   void append(const char *Format, ...) {
2268d75effSDimitry Andric     va_list Args;
2368d75effSDimitry Andric     va_start(Args, Format);
2468d75effSDimitry Andric     Message.append(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 
37*480093f4SDimitry Andric inline void NORETURN trap() { __builtin_trap(); }
3868d75effSDimitry Andric 
3968d75effSDimitry Andric // This could potentially be called recursively if a CHECK fails in the reports.
4068d75effSDimitry Andric void NORETURN reportCheckFailed(const char *File, int Line,
4168d75effSDimitry Andric                                 const char *Condition, u64 Value1, u64 Value2) {
4268d75effSDimitry Andric   static atomic_u32 NumberOfCalls;
4368d75effSDimitry Andric   if (atomic_fetch_add(&NumberOfCalls, 1, memory_order_relaxed) > 2) {
4468d75effSDimitry Andric     // TODO(kostyak): maybe sleep here?
4568d75effSDimitry Andric     trap();
4668d75effSDimitry Andric   }
4768d75effSDimitry Andric   ScopedErrorReport Report;
4868d75effSDimitry Andric   Report.append("CHECK failed @ %s:%d %s (%llu, %llu)\n", File, Line, Condition,
4968d75effSDimitry Andric                 Value1, Value2);
5068d75effSDimitry Andric }
5168d75effSDimitry Andric 
5268d75effSDimitry Andric // Generic string fatal error message.
5368d75effSDimitry Andric void NORETURN reportError(const char *Message) {
5468d75effSDimitry Andric   ScopedErrorReport Report;
5568d75effSDimitry Andric   Report.append("%s\n", Message);
5668d75effSDimitry Andric }
5768d75effSDimitry Andric 
5868d75effSDimitry Andric void NORETURN reportInvalidFlag(const char *FlagType, const char *Value) {
5968d75effSDimitry Andric   ScopedErrorReport Report;
6068d75effSDimitry Andric   Report.append("invalid value for %s option: '%s'\n", FlagType, Value);
6168d75effSDimitry Andric }
6268d75effSDimitry Andric 
6368d75effSDimitry Andric // The checksum of a chunk header is invalid. This could be caused by an
6468d75effSDimitry Andric // {over,under}write of the header, a pointer that is not an actual chunk.
6568d75effSDimitry Andric void NORETURN reportHeaderCorruption(void *Ptr) {
6668d75effSDimitry Andric   ScopedErrorReport Report;
6768d75effSDimitry Andric   Report.append("corrupted chunk header at address %p\n", Ptr);
6868d75effSDimitry Andric }
6968d75effSDimitry Andric 
7068d75effSDimitry Andric // Two threads have attempted to modify a chunk header at the same time. This is
7168d75effSDimitry Andric // symptomatic of a race-condition in the application code, or general lack of
7268d75effSDimitry Andric // proper locking.
7368d75effSDimitry Andric void NORETURN reportHeaderRace(void *Ptr) {
7468d75effSDimitry Andric   ScopedErrorReport Report;
7568d75effSDimitry Andric   Report.append("race on chunk header at address %p\n", Ptr);
7668d75effSDimitry Andric }
7768d75effSDimitry Andric 
7868d75effSDimitry Andric // The allocator was compiled with parameters that conflict with field size
7968d75effSDimitry Andric // requirements.
8068d75effSDimitry Andric void NORETURN reportSanityCheckError(const char *Field) {
8168d75effSDimitry Andric   ScopedErrorReport Report;
8268d75effSDimitry Andric   Report.append("maximum possible %s doesn't fit in header\n", Field);
8368d75effSDimitry Andric }
8468d75effSDimitry Andric 
8568d75effSDimitry Andric // We enforce a maximum alignment, to keep fields smaller and generally prevent
8668d75effSDimitry Andric // integer overflows, or unexpected corner cases.
8768d75effSDimitry Andric void NORETURN reportAlignmentTooBig(uptr Alignment, uptr MaxAlignment) {
8868d75effSDimitry Andric   ScopedErrorReport Report;
8968d75effSDimitry Andric   Report.append("invalid allocation alignment: %zu exceeds maximum supported "
9068d75effSDimitry Andric                 "alignment of %zu\n",
9168d75effSDimitry Andric                 Alignment, MaxAlignment);
9268d75effSDimitry Andric }
9368d75effSDimitry Andric 
9468d75effSDimitry Andric // See above, we also enforce a maximum size.
9568d75effSDimitry Andric void NORETURN reportAllocationSizeTooBig(uptr UserSize, uptr TotalSize,
9668d75effSDimitry Andric                                          uptr MaxSize) {
9768d75effSDimitry Andric   ScopedErrorReport Report;
9868d75effSDimitry Andric   Report.append("requested allocation size %zu (%zu after adjustments) exceeds "
9968d75effSDimitry Andric                 "maximum supported size of %zu\n",
10068d75effSDimitry Andric                 UserSize, TotalSize, MaxSize);
10168d75effSDimitry Andric }
10268d75effSDimitry Andric 
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 
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.
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 
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).
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.
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 
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 
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 
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 
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 
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