xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/scudo/standalone/report.cpp (revision 68d75eff68281c1b445e3010bb975eae07aac225)
1*68d75effSDimitry Andric //===-- report.cpp ----------------------------------------------*- C++ -*-===//
2*68d75effSDimitry Andric //
3*68d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*68d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*68d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*68d75effSDimitry Andric //
7*68d75effSDimitry Andric //===----------------------------------------------------------------------===//
8*68d75effSDimitry Andric 
9*68d75effSDimitry Andric #include "report.h"
10*68d75effSDimitry Andric 
11*68d75effSDimitry Andric #include "atomic_helpers.h"
12*68d75effSDimitry Andric #include "string_utils.h"
13*68d75effSDimitry Andric 
14*68d75effSDimitry Andric #include <stdarg.h>
15*68d75effSDimitry Andric 
16*68d75effSDimitry Andric namespace scudo {
17*68d75effSDimitry Andric 
18*68d75effSDimitry Andric class ScopedErrorReport {
19*68d75effSDimitry Andric public:
20*68d75effSDimitry Andric   ScopedErrorReport() : Message(512) { Message.append("Scudo ERROR: "); }
21*68d75effSDimitry Andric   void append(const char *Format, ...) {
22*68d75effSDimitry Andric     va_list Args;
23*68d75effSDimitry Andric     va_start(Args, Format);
24*68d75effSDimitry Andric     Message.append(Format, Args);
25*68d75effSDimitry Andric     va_end(Args);
26*68d75effSDimitry Andric   }
27*68d75effSDimitry Andric   NORETURN ~ScopedErrorReport() {
28*68d75effSDimitry Andric     outputRaw(Message.data());
29*68d75effSDimitry Andric     setAbortMessage(Message.data());
30*68d75effSDimitry Andric     die();
31*68d75effSDimitry Andric   }
32*68d75effSDimitry Andric 
33*68d75effSDimitry Andric private:
34*68d75effSDimitry Andric   ScopedString Message;
35*68d75effSDimitry Andric };
36*68d75effSDimitry Andric 
37*68d75effSDimitry Andric INLINE void NORETURN trap() { __builtin_trap(); }
38*68d75effSDimitry Andric 
39*68d75effSDimitry Andric // This could potentially be called recursively if a CHECK fails in the reports.
40*68d75effSDimitry Andric void NORETURN reportCheckFailed(const char *File, int Line,
41*68d75effSDimitry Andric                                 const char *Condition, u64 Value1, u64 Value2) {
42*68d75effSDimitry Andric   static atomic_u32 NumberOfCalls;
43*68d75effSDimitry Andric   if (atomic_fetch_add(&NumberOfCalls, 1, memory_order_relaxed) > 2) {
44*68d75effSDimitry Andric     // TODO(kostyak): maybe sleep here?
45*68d75effSDimitry Andric     trap();
46*68d75effSDimitry Andric   }
47*68d75effSDimitry Andric   ScopedErrorReport Report;
48*68d75effSDimitry Andric   Report.append("CHECK failed @ %s:%d %s (%llu, %llu)\n", File, Line, Condition,
49*68d75effSDimitry Andric                 Value1, Value2);
50*68d75effSDimitry Andric }
51*68d75effSDimitry Andric 
52*68d75effSDimitry Andric // Generic string fatal error message.
53*68d75effSDimitry Andric void NORETURN reportError(const char *Message) {
54*68d75effSDimitry Andric   ScopedErrorReport Report;
55*68d75effSDimitry Andric   Report.append("%s\n", Message);
56*68d75effSDimitry Andric }
57*68d75effSDimitry Andric 
58*68d75effSDimitry Andric void NORETURN reportInvalidFlag(const char *FlagType, const char *Value) {
59*68d75effSDimitry Andric   ScopedErrorReport Report;
60*68d75effSDimitry Andric   Report.append("invalid value for %s option: '%s'\n", FlagType, Value);
61*68d75effSDimitry Andric }
62*68d75effSDimitry Andric 
63*68d75effSDimitry Andric // The checksum of a chunk header is invalid. This could be caused by an
64*68d75effSDimitry Andric // {over,under}write of the header, a pointer that is not an actual chunk.
65*68d75effSDimitry Andric void NORETURN reportHeaderCorruption(void *Ptr) {
66*68d75effSDimitry Andric   ScopedErrorReport Report;
67*68d75effSDimitry Andric   Report.append("corrupted chunk header at address %p\n", Ptr);
68*68d75effSDimitry Andric }
69*68d75effSDimitry Andric 
70*68d75effSDimitry Andric // Two threads have attempted to modify a chunk header at the same time. This is
71*68d75effSDimitry Andric // symptomatic of a race-condition in the application code, or general lack of
72*68d75effSDimitry Andric // proper locking.
73*68d75effSDimitry Andric void NORETURN reportHeaderRace(void *Ptr) {
74*68d75effSDimitry Andric   ScopedErrorReport Report;
75*68d75effSDimitry Andric   Report.append("race on chunk header at address %p\n", Ptr);
76*68d75effSDimitry Andric }
77*68d75effSDimitry Andric 
78*68d75effSDimitry Andric // The allocator was compiled with parameters that conflict with field size
79*68d75effSDimitry Andric // requirements.
80*68d75effSDimitry Andric void NORETURN reportSanityCheckError(const char *Field) {
81*68d75effSDimitry Andric   ScopedErrorReport Report;
82*68d75effSDimitry Andric   Report.append("maximum possible %s doesn't fit in header\n", Field);
83*68d75effSDimitry Andric }
84*68d75effSDimitry Andric 
85*68d75effSDimitry Andric // We enforce a maximum alignment, to keep fields smaller and generally prevent
86*68d75effSDimitry Andric // integer overflows, or unexpected corner cases.
87*68d75effSDimitry Andric void NORETURN reportAlignmentTooBig(uptr Alignment, uptr MaxAlignment) {
88*68d75effSDimitry Andric   ScopedErrorReport Report;
89*68d75effSDimitry Andric   Report.append("invalid allocation alignment: %zu exceeds maximum supported "
90*68d75effSDimitry Andric                 "alignment of %zu\n",
91*68d75effSDimitry Andric                 Alignment, MaxAlignment);
92*68d75effSDimitry Andric }
93*68d75effSDimitry Andric 
94*68d75effSDimitry Andric // See above, we also enforce a maximum size.
95*68d75effSDimitry Andric void NORETURN reportAllocationSizeTooBig(uptr UserSize, uptr TotalSize,
96*68d75effSDimitry Andric                                          uptr MaxSize) {
97*68d75effSDimitry Andric   ScopedErrorReport Report;
98*68d75effSDimitry Andric   Report.append("requested allocation size %zu (%zu after adjustments) exceeds "
99*68d75effSDimitry Andric                 "maximum supported size of %zu\n",
100*68d75effSDimitry Andric                 UserSize, TotalSize, MaxSize);
101*68d75effSDimitry Andric }
102*68d75effSDimitry Andric 
103*68d75effSDimitry Andric void NORETURN reportOutOfMemory(uptr RequestedSize) {
104*68d75effSDimitry Andric   ScopedErrorReport Report;
105*68d75effSDimitry Andric   Report.append("out of memory trying to allocate %zu bytes\n", RequestedSize);
106*68d75effSDimitry Andric }
107*68d75effSDimitry Andric 
108*68d75effSDimitry Andric static const char *stringifyAction(AllocatorAction Action) {
109*68d75effSDimitry Andric   switch (Action) {
110*68d75effSDimitry Andric   case AllocatorAction::Recycling:
111*68d75effSDimitry Andric     return "recycling";
112*68d75effSDimitry Andric   case AllocatorAction::Deallocating:
113*68d75effSDimitry Andric     return "deallocating";
114*68d75effSDimitry Andric   case AllocatorAction::Reallocating:
115*68d75effSDimitry Andric     return "reallocating";
116*68d75effSDimitry Andric   case AllocatorAction::Sizing:
117*68d75effSDimitry Andric     return "sizing";
118*68d75effSDimitry Andric   }
119*68d75effSDimitry Andric   return "<invalid action>";
120*68d75effSDimitry Andric }
121*68d75effSDimitry Andric 
122*68d75effSDimitry Andric // The chunk is not in a state congruent with the operation we want to perform.
123*68d75effSDimitry Andric // This is usually the case with a double-free, a realloc of a freed pointer.
124*68d75effSDimitry Andric void NORETURN reportInvalidChunkState(AllocatorAction Action, void *Ptr) {
125*68d75effSDimitry Andric   ScopedErrorReport Report;
126*68d75effSDimitry Andric   Report.append("invalid chunk state when %s address %p\n",
127*68d75effSDimitry Andric                 stringifyAction(Action), Ptr);
128*68d75effSDimitry Andric }
129*68d75effSDimitry Andric 
130*68d75effSDimitry Andric void NORETURN reportMisalignedPointer(AllocatorAction Action, void *Ptr) {
131*68d75effSDimitry Andric   ScopedErrorReport Report;
132*68d75effSDimitry Andric   Report.append("misaligned pointer when %s address %p\n",
133*68d75effSDimitry Andric                 stringifyAction(Action), Ptr);
134*68d75effSDimitry Andric }
135*68d75effSDimitry Andric 
136*68d75effSDimitry Andric // The deallocation function used is at odds with the one used to allocate the
137*68d75effSDimitry Andric // chunk (eg: new[]/delete or malloc/delete, and so on).
138*68d75effSDimitry Andric void NORETURN reportDeallocTypeMismatch(AllocatorAction Action, void *Ptr,
139*68d75effSDimitry Andric                                         u8 TypeA, u8 TypeB) {
140*68d75effSDimitry Andric   ScopedErrorReport Report;
141*68d75effSDimitry Andric   Report.append("allocation type mismatch when %s address %p (%d vs %d)\n",
142*68d75effSDimitry Andric                 stringifyAction(Action), Ptr, TypeA, TypeB);
143*68d75effSDimitry Andric }
144*68d75effSDimitry Andric 
145*68d75effSDimitry Andric // The size specified to the delete operator does not match the one that was
146*68d75effSDimitry Andric // passed to new when allocating the chunk.
147*68d75effSDimitry Andric void NORETURN reportDeleteSizeMismatch(void *Ptr, uptr Size,
148*68d75effSDimitry Andric                                        uptr ExpectedSize) {
149*68d75effSDimitry Andric   ScopedErrorReport Report;
150*68d75effSDimitry Andric   Report.append(
151*68d75effSDimitry Andric       "invalid sized delete when deallocating address %p (%zu vs %zu)\n", Ptr,
152*68d75effSDimitry Andric       Size, ExpectedSize);
153*68d75effSDimitry Andric }
154*68d75effSDimitry Andric 
155*68d75effSDimitry Andric void NORETURN reportAlignmentNotPowerOfTwo(uptr Alignment) {
156*68d75effSDimitry Andric   ScopedErrorReport Report;
157*68d75effSDimitry Andric   Report.append(
158*68d75effSDimitry Andric       "invalid allocation alignment: %zu, alignment must be a power of two\n",
159*68d75effSDimitry Andric       Alignment);
160*68d75effSDimitry Andric }
161*68d75effSDimitry Andric 
162*68d75effSDimitry Andric void NORETURN reportCallocOverflow(uptr Count, uptr Size) {
163*68d75effSDimitry Andric   ScopedErrorReport Report;
164*68d75effSDimitry Andric   Report.append("calloc parameters overflow: count * size (%zu * %zu) cannot "
165*68d75effSDimitry Andric                 "be represented with type size_t\n",
166*68d75effSDimitry Andric                 Count, Size);
167*68d75effSDimitry Andric }
168*68d75effSDimitry Andric 
169*68d75effSDimitry Andric void NORETURN reportInvalidPosixMemalignAlignment(uptr Alignment) {
170*68d75effSDimitry Andric   ScopedErrorReport Report;
171*68d75effSDimitry Andric   Report.append(
172*68d75effSDimitry Andric       "invalid alignment requested in posix_memalign: %zu, alignment must be a "
173*68d75effSDimitry Andric       "power of two and a multiple of sizeof(void *) == %zu\n",
174*68d75effSDimitry Andric       Alignment, sizeof(void *));
175*68d75effSDimitry Andric }
176*68d75effSDimitry Andric 
177*68d75effSDimitry Andric void NORETURN reportPvallocOverflow(uptr Size) {
178*68d75effSDimitry Andric   ScopedErrorReport Report;
179*68d75effSDimitry Andric   Report.append("pvalloc parameters overflow: size %zu rounded up to system "
180*68d75effSDimitry Andric                 "page size %zu cannot be represented in type size_t\n",
181*68d75effSDimitry Andric                 Size, getPageSizeCached());
182*68d75effSDimitry Andric }
183*68d75effSDimitry Andric 
184*68d75effSDimitry Andric void NORETURN reportInvalidAlignedAllocAlignment(uptr Alignment, uptr Size) {
185*68d75effSDimitry Andric   ScopedErrorReport Report;
186*68d75effSDimitry Andric   Report.append("invalid alignment requested in aligned_alloc: %zu, alignment "
187*68d75effSDimitry Andric                 "must be a power of two and the requested size %zu must be a "
188*68d75effSDimitry Andric                 "multiple of alignment\n",
189*68d75effSDimitry Andric                 Alignment, Size);
190*68d75effSDimitry Andric }
191*68d75effSDimitry Andric 
192*68d75effSDimitry Andric } // namespace scudo
193