C/C++ API Reference
Loading...
Searching...
No Matches
poisonable.h
1// Copyright 2024 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include <algorithm>
17#include <cstddef>
18#include <cstdint>
19#include <cstring>
20
21#include "lib/stdcompat/bit.h"
22#include "pw_allocator/block/contiguous.h"
23#include "pw_allocator/config.h"
24
25namespace pw::allocator {
26namespace internal {
27
28// Trivial base class for trait support.
30
31} // namespace internal
32
34
52template <typename Derived>
54 protected:
55 constexpr PoisonableBlock() {
56 static_assert(is_contiguous_v<Derived>,
57 "Types derived from PoisonableBlock must also derive from "
58 "ContiguousBlock");
59 }
60
61 public:
62 static constexpr size_t kPoisonOffset = Derived::ReservedWhenFree();
63
65 constexpr uintptr_t GetPoisonWord() const {
66 return derived()->DoGetPoisonWord();
67 }
68
70 constexpr bool IsPoisoned() const;
71
78 constexpr void Poison();
79
80 protected:
81 constexpr uintptr_t DoGetPoisonWord() const {
82 // Hex dump looks like "defaced code is bad".
83 return static_cast<uintptr_t>(0xAD5bE10DDCCEFADEULL);
84 }
85
87 constexpr Derived* DoSplitFirst(size_t new_inner_size);
88
90 constexpr Derived* DoSplitLast(size_t new_inner_size);
91
93 constexpr void DoMergeNext();
94
96 constexpr bool DoCheckInvariants(bool strict) const;
97
99 constexpr void SetFree(bool is_free);
100
101 private:
102 constexpr Derived* derived() { return static_cast<Derived*>(this); }
103 constexpr const Derived* derived() const {
104 return static_cast<const Derived*>(this);
105 }
106
109 constexpr uintptr_t* PoisonableBegin() const;
110
113 constexpr uintptr_t* PoisonableEnd() const;
114};
115
117template <typename BlockType>
118struct is_poisonable : std::is_base_of<internal::PoisonableBase, BlockType> {};
119
121template <typename BlockType>
123
125
126namespace internal {
127
130void CheckPoisonedWhileInUse(const void* block, bool is_free);
131
134void CheckPoisonCorrupted(const void* block, bool pattern_is_intact);
135
136} // namespace internal
137
138// Template method implementations.
139
140template <typename Derived>
142 if constexpr (Hardening::kIncludesDebugChecks) {
143 derived()->CheckInvariants();
144 }
145 return derived()->IsPoisonedUnchecked();
146}
147
148template <typename Derived>
150 if constexpr (Hardening::kIncludesDebugChecks) {
151 derived()->CheckInvariants();
152 }
153 auto* begin = PoisonableBegin();
154 auto* end = PoisonableEnd();
155 if (begin < end) {
156 std::fill(begin, end, derived()->GetPoisonWord());
157 derived()->SetPoisoned(true);
158 }
159 if constexpr (Hardening::kIncludesDebugChecks) {
160 derived()->CheckInvariants();
161 }
162}
163
164template <typename Derived>
166 size_t new_inner_size) {
167 bool should_poison = derived()->IsPoisoned();
168 derived()->SetPoisoned(false);
169 Derived* trailing =
170 derived()->ContiguousBlock<Derived>::DoSplitFirst(new_inner_size);
171 if (should_poison) {
172 trailing->SetPoisoned(true);
173 }
174 return trailing;
175}
176
177template <typename Derived>
179 size_t new_inner_size) {
180 bool should_poison = derived()->IsPoisoned();
181 derived()->SetPoisoned(false);
182 Derived* trailing =
183 derived()->ContiguousBlock<Derived>::DoSplitLast(new_inner_size);
184 if (should_poison) {
185 derived()->SetPoisoned(true);
186 }
187 return trailing;
188}
189
190template <typename Derived>
192 // Repoisoning is handle by the `BlockAllocator::DoDeallocate`.
193 derived()->SetPoisoned(false);
194 derived()->ContiguousBlock<Derived>::DoMergeNext();
195}
196
197template <typename Derived>
198constexpr bool PoisonableBlock<Derived>::DoCheckInvariants(bool strict) const {
199 if (!derived()->IsPoisonedUnchecked()) {
200 return true;
201 }
202
203 bool valid = derived()->IsFreeUnchecked();
204 if constexpr (Hardening::kIncludesDebugChecks) {
205 internal::CheckPoisonedWhileInUse(this, valid || !strict);
206 }
207
208 auto* begin = PoisonableBegin();
209 auto* end = PoisonableEnd();
210 if (begin < end) {
211 valid &= std::all_of(
212 begin, end, [this](uintptr_t word) { return word == GetPoisonWord(); });
213 }
214 if constexpr (Hardening::kIncludesDebugChecks) {
215 internal::CheckPoisonCorrupted(this, valid || !strict);
216 }
217
218 return valid;
219}
220
221template <typename Derived>
222constexpr void PoisonableBlock<Derived>::SetFree(bool is_free) {
223 if (!is_free) {
224 derived()->SetPoisoned(false);
225 }
226}
227
228template <typename Derived>
229constexpr uintptr_t* PoisonableBlock<Derived>::PoisonableBegin() const {
230 auto addr = cpp20::bit_cast<uintptr_t>(derived()->UsableSpaceUnchecked());
231 addr = AlignUp(addr + kPoisonOffset, sizeof(uintptr_t*));
232 return cpp20::bit_cast<uintptr_t*>(addr);
233}
234
235template <typename Derived>
236constexpr uintptr_t* PoisonableBlock<Derived>::PoisonableEnd() const {
237 auto addr = cpp20::bit_cast<uintptr_t>(derived()->UsableSpaceUnchecked());
238 addr = AlignDown(addr + derived()->InnerSizeUnchecked(), sizeof(uintptr_t*));
239 return cpp20::bit_cast<uintptr_t*>(addr);
240}
241
242} // namespace pw::allocator
Definition: poisonable.h:53
constexpr void SetFree(bool is_free)
Clears the poisoned state if a block is not free.
Definition: poisonable.h:222
constexpr Derived * DoSplitLast(size_t new_inner_size)
Definition: poisonable.h:178
constexpr void DoMergeNext()
Definition: poisonable.h:191
constexpr bool DoCheckInvariants(bool strict) const
Like IsValid, but crashes if invalid.
Definition: poisonable.h:198
constexpr Derived * DoSplitFirst(size_t new_inner_size)
Definition: poisonable.h:165
constexpr void Poison()
Definition: poisonable.h:149
constexpr uintptr_t GetPoisonWord() const
Returns the value written to a block's usable space when poisoning.
Definition: poisonable.h:65
constexpr bool IsPoisoned() const
Returns whether this block has been poisoned.
Definition: poisonable.h:141
constexpr bool is_poisonable_v
Helper variable template for is_poisonable<BlockType>::value.
Definition: poisonable.h:122
constexpr size_t AlignUp(uintptr_t value, size_t alignment)
Returns the value rounded up to the nearest multiple of alignment.
Definition: alignment.h:54
constexpr size_t AlignDown(uintptr_t value, size_t alignment)
Returns the value rounded down to the nearest multiple of alignment.
Definition: alignment.h:42
Definition: poisonable.h:29
Trait type that allow interrogating a block as to whether it is poisonable.
Definition: poisonable.h:118