1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef PARTITION_ALLOC_PARTITION_ALLOC_CHECK_H_
6 #define PARTITION_ALLOC_PARTITION_ALLOC_CHECK_H_
7 
8 #include <cstdint>
9 
10 #include "build/build_config.h"
11 #include "partition_alloc/page_allocator_constants.h"
12 #include "partition_alloc/partition_alloc_base/check.h"
13 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
14 #include "partition_alloc/partition_alloc_base/debug/alias.h"
15 #include "partition_alloc/partition_alloc_base/debug/debugging_buildflags.h"
16 #include "partition_alloc/partition_alloc_base/immediate_crash.h"
17 #include "partition_alloc/partition_alloc_buildflags.h"
18 
19 // When PartitionAlloc is used as the default allocator, we cannot use the
20 // regular (D)CHECK() macros, as they allocate internally. When an assertion is
21 // triggered, they format strings, leading to reentrancy in the code, which none
22 // of PartitionAlloc is designed to support (and especially not for error
23 // paths).
24 //
25 // As a consequence:
26 // - When PartitionAlloc is not malloc(), use the regular macros
27 // - Otherwise, crash immediately. This provides worse error messages though.
28 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && !PA_BASE_CHECK_WILL_STREAM()
29 
30 // For official build discard log strings to reduce binary bloat.
31 // See base/check.h for implementation details.
32 #define PA_CHECK(condition)                        \
33   PA_UNLIKELY(!(condition)) ? PA_IMMEDIATE_CRASH() \
34                             : PA_EAT_CHECK_STREAM_PARAMS()
35 
36 #if BUILDFLAG(PA_DCHECK_IS_ON)
37 #define PA_DCHECK(condition) PA_CHECK(condition)
38 #else
39 #define PA_DCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition))
40 #endif  // BUILDFLAG(PA_DCHECK_IS_ON)
41 
42 #define PA_PCHECK(condition)                                 \
43   if (!(condition)) {                                        \
44     int error = errno;                                       \
45     ::partition_alloc::internal::base::debug::Alias(&error); \
46     PA_IMMEDIATE_CRASH();                                    \
47   }                                                          \
48   static_assert(true)
49 
50 #if BUILDFLAG(PA_DCHECK_IS_ON)
51 #define PA_DPCHECK(condition) PA_PCHECK(condition)
52 #else
53 #define PA_DPCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition))
54 #endif  // BUILDFLAG(PA_DCHECK_IS_ON)
55 
56 #else  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) &&
57        // !PA_BASE_CHECK_WILL_STREAM()
58 #define PA_CHECK(condition) PA_BASE_CHECK(condition)
59 #define PA_DCHECK(condition) PA_BASE_DCHECK(condition)
60 #define PA_PCHECK(condition) PA_BASE_PCHECK(condition)
61 #define PA_DPCHECK(condition) PA_BASE_DPCHECK(condition)
62 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) &&
63         // !PA_BASE_CHECK_WILL_STREAM()
64 
65 // Expensive dchecks that run within *Scan. These checks are only enabled in
66 // debug builds with dchecks enabled.
67 #if !defined(NDEBUG)
68 #define PA_SCAN_DCHECK_IS_ON() BUILDFLAG(PA_DCHECK_IS_ON)
69 #else
70 #define PA_SCAN_DCHECK_IS_ON() 0
71 #endif
72 
73 #if PA_SCAN_DCHECK_IS_ON()
74 #define PA_SCAN_DCHECK(expr) PA_DCHECK(expr)
75 #else
76 #define PA_SCAN_DCHECK(expr) PA_EAT_CHECK_STREAM_PARAMS(!(expr))
77 #endif
78 
79 #if defined(PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR)
80 
81 // Use this macro to assert on things that are conditionally constexpr as
82 // determined by PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR or
83 // PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR. Where fixed at compile time, this
84 // is a static_assert. Where determined at run time, this is a PA_CHECK.
85 // Therefore, this macro must only be used where both a static_assert and a
86 // PA_CHECK would be viable, that is, within a function, and ideally a function
87 // that executes only once, early in the program, such as during initialization.
88 #define STATIC_ASSERT_OR_PA_CHECK(condition, message) \
89   static_assert(condition, message)
90 
91 #else
92 
93 #define STATIC_ASSERT_OR_PA_CHECK(condition, message) \
94   do {                                                \
95     PA_CHECK(condition) << (message);                 \
96   } while (false)
97 
98 #endif
99 
100 // alignas(16) DebugKv causes breakpad_unittests and sandbox_linux_unittests
101 // failures on android-marshmallow-x86-rel because of SIGSEGV.
102 #if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_X86_FAMILY) && \
103     defined(ARCH_CPU_32_BITS)
104 #define PA_DEBUGKV_ALIGN alignas(8)
105 #else
106 #define PA_DEBUGKV_ALIGN alignas(16)
107 #endif
108 
109 namespace partition_alloc::internal {
110 
111 static constexpr size_t kDebugKeyMaxLength = 8ull;
112 
113 // Used for PA_DEBUG_DATA_ON_STACK, below.
114 struct PA_DEBUGKV_ALIGN DebugKv {
115   // 16 bytes object aligned on 16 bytes, to make it easier to see in crash
116   // reports.
117   char k[kDebugKeyMaxLength] = {};  // Not necessarily 0-terminated.
118   uint64_t v = 0;
119 
DebugKvDebugKv120   DebugKv(const char* key, uint64_t value) : v(value) {
121     // Fill with ' ', so that the stack dump is nicer to read.  Not using
122     // memset() on purpose, this header is included from *many* places.
123     for (size_t index = 0; index < sizeof k; index++) {
124       k[index] = ' ';
125     }
126 
127     for (size_t index = 0; index < sizeof k; index++) {
128       k[index] = key[index];
129       if (key[index] == '\0') {
130         break;
131       }
132     }
133   }
134 };
135 
136 }  // namespace partition_alloc::internal
137 
138 #define PA_CONCAT(x, y) x##y
139 #define PA_CONCAT2(x, y) PA_CONCAT(x, y)
140 #define PA_DEBUG_UNIQUE_NAME PA_CONCAT2(kv, __LINE__)
141 
142 // Puts a key-value pair on the stack for debugging. `base::debug::Alias()`
143 // makes sure a local variable is saved on the stack, but the variables can be
144 // hard to find in crash reports, particularly if the frame pointer is not
145 // present / invalid.
146 //
147 // This puts a key right before the value on the stack. The key has to be a C
148 // string, which gets truncated if it's longer than 8 characters.
149 // Example use:
150 // PA_DEBUG_DATA_ON_STACK("size", 0x42)
151 //
152 // Sample output in lldb:
153 // (lldb) x 0x00007fffffffd0d0 0x00007fffffffd0f0
154 // 0x7fffffffd0d0: 73 69 7a 65 00 00 00 00 42 00 00 00 00 00 00 00
155 // size............
156 //
157 // With gdb, one can use:
158 // x/8g <STACK_POINTER>
159 // to see the data. With lldb, "x <STACK_POINTER> <FRAME_POJNTER>" can be used.
160 #define PA_DEBUG_DATA_ON_STACK(name, value)                               \
161   static_assert(sizeof name <=                                            \
162                 ::partition_alloc::internal::kDebugKeyMaxLength + 1);     \
163   ::partition_alloc::internal::DebugKv PA_DEBUG_UNIQUE_NAME{name, value}; \
164   ::partition_alloc::internal::base::debug::Alias(&PA_DEBUG_UNIQUE_NAME)
165 
166 #endif  // PARTITION_ALLOC_PARTITION_ALLOC_CHECK_H_
167