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 explicit PoisonableBlock() {
56 // Assert within a function, since `Derived` is not complete when this type
57 // is defined.
58 static_assert(is_contiguous_v<Derived>,
59 "Types derived from PoisonableBlock must also derive from "
60 "ContiguousBlock");
61 }
62
63 public:
64 static constexpr size_t kPoisonOffset = Derived::ReservedWhenFree();
65
67 constexpr uintptr_t GetPoisonWord() const {
68 return derived()->DoGetPoisonWord();
69 }
70
72 constexpr bool IsPoisoned() const;
73
80 constexpr void Poison();
81
82 protected:
83 constexpr uintptr_t DoGetPoisonWord() const {
84 // Hex dump looks like "defaced code is bad".
85 return static_cast<uintptr_t>(0xAD5bE10DDCCEFADEULL);
86 }
87
89 constexpr Derived* DoSplitFirst(size_t new_inner_size);
90
92 constexpr Derived* DoSplitLast(size_t new_inner_size);
93
95 constexpr void DoMergeNext();
96
98 constexpr bool DoCheckInvariants(bool strict) const;
99
101 constexpr void SetFree(bool is_free);
102
103 private:
104 constexpr Derived* derived() { return static_cast<Derived*>(this); }
105 constexpr const Derived* derived() const {
106 return static_cast<const Derived*>(this);
107 }
108
111 constexpr uintptr_t* PoisonableBegin() const {
112 auto addr = cpp20::bit_cast<uintptr_t>(derived()->UsableSpaceUnchecked());
113 addr = AlignUp(addr + kPoisonOffset, sizeof(uintptr_t*));
114 return cpp20::bit_cast<uintptr_t*>(addr);
115 }
116
119 constexpr uintptr_t* PoisonableEnd() const {
120 auto addr = cpp20::bit_cast<uintptr_t>(derived()->UsableSpaceUnchecked());
121 addr =
122 AlignDown(addr + derived()->InnerSizeUnchecked(), sizeof(uintptr_t*));
123 return cpp20::bit_cast<uintptr_t*>(addr);
124 }
125};
126
128template <typename BlockType>
129struct is_poisonable : std::is_base_of<internal::PoisonableBase, BlockType> {};
130
132template <typename BlockType>
134
136
137namespace internal {
138
141void CheckPoisonedWhileInUse(const void* block, bool is_free);
142
145void CheckPoisonCorrupted(const void* block, bool pattern_is_intact);
146
147} // namespace internal
148
149// Template method implementations.
150
151template <typename Derived>
153 if constexpr (Hardening::kIncludesDebugChecks) {
154 derived()->CheckInvariants();
155 }
156 return derived()->IsPoisonedUnchecked();
157}
158
159template <typename Derived>
161 if constexpr (Hardening::kIncludesDebugChecks) {
162 derived()->CheckInvariants();
163 }
164 auto* begin = PoisonableBegin();
165 auto* end = PoisonableEnd();
166 if (begin < end) {
167 std::fill(begin, end, derived()->GetPoisonWord());
168 derived()->SetPoisoned(true);
169 }
170 if constexpr (Hardening::kIncludesDebugChecks) {
171 derived()->CheckInvariants();
172 }
173}
174
175template <typename Derived>
177 size_t new_inner_size) {
178 bool should_poison = derived()->IsPoisoned();
179 derived()->SetPoisoned(false);
180 Derived* trailing =
181 derived()->ContiguousBlock<Derived>::DoSplitFirst(new_inner_size);
182 if (should_poison) {
183 trailing->SetPoisoned(true);
184 }
185 return trailing;
186}
187
188template <typename Derived>
190 size_t new_inner_size) {
191 bool should_poison = derived()->IsPoisoned();
192 derived()->SetPoisoned(false);
193 Derived* trailing =
194 derived()->ContiguousBlock<Derived>::DoSplitLast(new_inner_size);
195 if (should_poison) {
196 derived()->SetPoisoned(true);
197 }
198 return trailing;
199}
200
201template <typename Derived>
203 // Repoisoning is handle by the `BlockAllocator::DoDeallocate`.
204 derived()->SetPoisoned(false);
205 derived()->ContiguousBlock<Derived>::DoMergeNext();
206}
207
208template <typename Derived>
209constexpr bool PoisonableBlock<Derived>::DoCheckInvariants(bool strict) const {
210 if (!derived()->IsPoisonedUnchecked()) {
211 return true;
212 }
213
214 bool valid = derived()->IsFreeUnchecked();
215 if constexpr (Hardening::kIncludesDebugChecks) {
216 internal::CheckPoisonedWhileInUse(this, valid || !strict);
217 }
218
219 auto* begin = PoisonableBegin();
220 auto* end = PoisonableEnd();
221 if (begin < end) {
222 valid &= std::all_of(
223 begin, end, [this](uintptr_t word) { return word == GetPoisonWord(); });
224 }
225 if constexpr (Hardening::kIncludesDebugChecks) {
226 internal::CheckPoisonCorrupted(this, valid || !strict);
227 }
228
229 return valid;
230}
231
232template <typename Derived>
233constexpr void PoisonableBlock<Derived>::SetFree(bool is_free) {
234 if (!is_free) {
235 derived()->SetPoisoned(false);
236 }
237}
238
239} // 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:233
constexpr Derived * DoSplitLast(size_t new_inner_size)
Definition: poisonable.h:189
constexpr void DoMergeNext()
Definition: poisonable.h:202
constexpr bool DoCheckInvariants(bool strict) const
Like IsValid, but crashes if invalid.
Definition: poisonable.h:209
constexpr Derived * DoSplitFirst(size_t new_inner_size)
Definition: poisonable.h:176
constexpr void Poison()
Definition: poisonable.h:160
constexpr uintptr_t GetPoisonWord() const
Returns the value written to a block's usable space when poisoning.
Definition: poisonable.h:67
constexpr bool IsPoisoned() const
Returns whether this block has been poisoned.
Definition: poisonable.h:152
constexpr bool is_poisonable_v
Helper variable template for is_poisonable<BlockType>::value.
Definition: poisonable.h:133
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:41
Definition: poisonable.h:29
Trait type that allow interrogating a block as to whether it is poisonable.
Definition: poisonable.h:129