Pigweed
 
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
50template <typename Derived>
52 protected:
53 constexpr explicit PoisonableBlock() {
54 // Assert within a function, since `Derived` is not complete when this type
55 // is defined.
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 auto addr = cpp20::bit_cast<uintptr_t>(derived()->UsableSpaceUnchecked());
111 addr = AlignUp(addr + kPoisonOffset, sizeof(uintptr_t*));
112 return cpp20::bit_cast<uintptr_t*>(addr);
113 }
114
117 constexpr uintptr_t* PoisonableEnd() const {
118 auto addr = cpp20::bit_cast<uintptr_t>(derived()->UsableSpaceUnchecked());
119 addr =
120 AlignDown(addr + derived()->InnerSizeUnchecked(), sizeof(uintptr_t*));
121 return cpp20::bit_cast<uintptr_t*>(addr);
122 }
123};
124
126template <typename BlockType>
127struct is_poisonable : std::is_base_of<internal::PoisonableBase, BlockType> {};
128
130template <typename BlockType>
131constexpr bool is_poisonable_v = is_poisonable<BlockType>::value;
132
133namespace internal {
134
137void CheckPoisonedWhileInUse(const void* block, bool is_free);
138
141void CheckPoisonCorrupted(const void* block, bool pattern_is_intact);
142
143} // namespace internal
144
145// Template method implementations.
146
147template <typename Derived>
149 if constexpr (Hardening::kIncludesDebugChecks) {
150 derived()->CheckInvariants();
151 }
152 return derived()->IsPoisonedUnchecked();
153}
154
155template <typename Derived>
157 if constexpr (Hardening::kIncludesDebugChecks) {
158 derived()->CheckInvariants();
159 }
160 auto* begin = PoisonableBegin();
161 auto* end = PoisonableEnd();
162 if (begin < end) {
163 std::fill(begin, end, derived()->GetPoisonWord());
164 derived()->SetPoisoned(true);
165 }
166 if constexpr (Hardening::kIncludesDebugChecks) {
167 derived()->CheckInvariants();
168 }
169}
170
171template <typename Derived>
173 size_t new_inner_size) {
174 bool should_poison = derived()->IsPoisoned();
175 derived()->SetPoisoned(false);
176 Derived* trailing =
177 derived()->ContiguousBlock<Derived>::DoSplitFirst(new_inner_size);
178 if (should_poison) {
179 trailing->SetPoisoned(true);
180 }
181 return trailing;
182}
183
184template <typename Derived>
186 size_t new_inner_size) {
187 bool should_poison = derived()->IsPoisoned();
188 derived()->SetPoisoned(false);
189 Derived* trailing =
190 derived()->ContiguousBlock<Derived>::DoSplitLast(new_inner_size);
191 if (should_poison) {
192 derived()->SetPoisoned(true);
193 }
194 return trailing;
195}
196
197template <typename Derived>
199 // Repoisoning is handle by the `BlockAllocator::DoDeallocate`.
200 derived()->SetPoisoned(false);
201 derived()->ContiguousBlock<Derived>::DoMergeNext();
202}
203
204template <typename Derived>
205constexpr bool PoisonableBlock<Derived>::DoCheckInvariants(bool strict) const {
206 if (!derived()->IsPoisonedUnchecked()) {
207 return true;
208 }
209
210 bool valid = derived()->IsFreeUnchecked();
211 if constexpr (Hardening::kIncludesDebugChecks) {
212 internal::CheckPoisonedWhileInUse(this, valid || !strict);
213 }
214
215 auto* begin = PoisonableBegin();
216 auto* end = PoisonableEnd();
217 if (begin < end) {
218 valid &= std::all_of(
219 begin, end, [this](uintptr_t word) { return word == GetPoisonWord(); });
220 }
221 if constexpr (Hardening::kIncludesDebugChecks) {
222 internal::CheckPoisonCorrupted(this, valid || !strict);
223 }
224
225 return valid;
226}
227
228template <typename Derived>
229constexpr void PoisonableBlock<Derived>::SetFree(bool is_free) {
230 if (!is_free) {
231 derived()->SetPoisoned(false);
232 }
233}
234
235} // namespace pw::allocator
Definition: poisonable.h:51
constexpr void SetFree(bool is_free)
Clears the poisoned state if a block is not free.
Definition: poisonable.h:229
constexpr Derived * DoSplitLast(size_t new_inner_size)
Definition: poisonable.h:185
constexpr void DoMergeNext()
Definition: poisonable.h:198
constexpr bool DoCheckInvariants(bool strict) const
Like IsValid, but crashes if invalid.
Definition: poisonable.h:205
constexpr Derived * DoSplitFirst(size_t new_inner_size)
Definition: poisonable.h:172
constexpr void Poison()
Definition: poisonable.h:156
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:148
constexpr size_t AlignUp(uintptr_t value, size_t alignment)
Returns the value rounded up to the nearest multiple of alignment.
Definition: alignment.h:52
constexpr size_t AlignDown(uintptr_t value, size_t alignment)
Returns the value rounded down to the nearest multiple of alignment.
Definition: alignment.h:39
Definition: poisonable.h:29
Trait type that allow interrogating a block as to whether it is poisonable.
Definition: poisonable.h:127