1 | //===-- chunk.h -------------------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef SCUDO_CHUNK_H_ |
10 | #define SCUDO_CHUNK_H_ |
11 | |
12 | #include "platform.h" |
13 | |
14 | #include "atomic_helpers.h" |
15 | #include "checksum.h" |
16 | #include "common.h" |
17 | #include "report.h" |
18 | |
19 | namespace scudo { |
20 | |
21 | extern Checksum HashAlgorithm; |
22 | |
23 | inline u16 computeChecksum(u32 Seed, uptr Value, uptr *Array, uptr ArraySize) { |
24 | // If the hardware CRC32 feature is defined here, it was enabled everywhere, |
25 | // as opposed to only for crc32_hw.cpp. This means that other hardware |
26 | // specific instructions were likely emitted at other places, and as a result |
27 | // there is no reason to not use it here. |
28 | #if defined(__CRC32__) || defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) |
29 | u32 Crc = static_cast<u32>(CRC32_INTRINSIC(Seed, Value)); |
30 | for (uptr I = 0; I < ArraySize; I++) |
31 | Crc = static_cast<u32>(CRC32_INTRINSIC(Crc, Array[I])); |
32 | return static_cast<u16>(Crc ^ (Crc >> 16)); |
33 | #else |
34 | if (HashAlgorithm == Checksum::HardwareCRC32) { |
35 | u32 Crc = computeHardwareCRC32(Crc: Seed, Data: Value); |
36 | for (uptr I = 0; I < ArraySize; I++) |
37 | Crc = computeHardwareCRC32(Crc, Data: Array[I]); |
38 | return static_cast<u16>(Crc ^ (Crc >> 16)); |
39 | } else { |
40 | u16 Checksum = computeBSDChecksum(Sum: static_cast<u16>(Seed), Data: Value); |
41 | for (uptr I = 0; I < ArraySize; I++) |
42 | Checksum = computeBSDChecksum(Sum: Checksum, Data: Array[I]); |
43 | return Checksum; |
44 | } |
45 | #endif // defined(__CRC32__) || defined(__SSE4_2__) || |
46 | // defined(__ARM_FEATURE_CRC32) |
47 | } |
48 | |
49 | namespace Chunk { |
50 | |
51 | // Note that in an ideal world, `State` and `Origin` should be `enum class`, and |
52 | // the associated `UnpackedHeader` fields of their respective enum class type |
53 | // but https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414 prevents it from |
54 | // happening, as it will error, complaining the number of bits is not enough. |
55 | enum Origin : u8 { |
56 | Malloc = 0, |
57 | New = 1, |
58 | NewArray = 2, |
59 | Memalign = 3, |
60 | }; |
61 | |
62 | enum State : u8 { Available = 0, Allocated = 1, Quarantined = 2 }; |
63 | |
64 | typedef u64 ; |
65 | // Update the 'Mask' constants to reflect changes in this structure. |
66 | struct { |
67 | uptr : 8; |
68 | u8 : 2; |
69 | // Origin if State == Allocated, or WasZeroed otherwise. |
70 | u8 : 2; |
71 | uptr : 20; |
72 | uptr : 16; |
73 | uptr : 16; |
74 | }; |
75 | typedef atomic_u64 ; |
76 | static_assert(sizeof(UnpackedHeader) == sizeof(PackedHeader), "" ); |
77 | |
78 | // Those constants are required to silence some -Werror=conversion errors when |
79 | // assigning values to the related bitfield variables. |
80 | constexpr uptr ClassIdMask = (1UL << 8) - 1; |
81 | constexpr u8 StateMask = (1U << 2) - 1; |
82 | constexpr u8 OriginMask = (1U << 2) - 1; |
83 | constexpr uptr SizeOrUnusedBytesMask = (1UL << 20) - 1; |
84 | constexpr uptr OffsetMask = (1UL << 16) - 1; |
85 | constexpr uptr ChecksumMask = (1UL << 16) - 1; |
86 | |
87 | constexpr uptr () { |
88 | return roundUp(X: sizeof(PackedHeader), Boundary: 1U << SCUDO_MIN_ALIGNMENT_LOG); |
89 | } |
90 | |
91 | inline AtomicPackedHeader *(void *Ptr) { |
92 | return reinterpret_cast<AtomicPackedHeader *>(reinterpret_cast<uptr>(Ptr) - |
93 | getHeaderSize()); |
94 | } |
95 | |
96 | inline const AtomicPackedHeader *(const void *Ptr) { |
97 | return reinterpret_cast<const AtomicPackedHeader *>( |
98 | reinterpret_cast<uptr>(Ptr) - getHeaderSize()); |
99 | } |
100 | |
101 | // We do not need a cryptographically strong hash for the checksum, but a CRC |
102 | // type function that can alert us in the event a header is invalid or |
103 | // corrupted. Ideally slightly better than a simple xor of all fields. |
104 | static inline u16 (u32 Cookie, const void *Ptr, |
105 | UnpackedHeader *) { |
106 | UnpackedHeader = *Header; |
107 | ZeroChecksumHeader.Checksum = 0; |
108 | uptr [sizeof(UnpackedHeader) / sizeof(uptr)]; |
109 | memcpy(dest: &HeaderHolder, src: &ZeroChecksumHeader, n: sizeof(HeaderHolder)); |
110 | return computeChecksum(Seed: Cookie, Value: reinterpret_cast<uptr>(Ptr), Array: HeaderHolder, |
111 | ARRAY_SIZE(HeaderHolder)); |
112 | } |
113 | |
114 | inline void (u32 Cookie, void *Ptr, |
115 | UnpackedHeader *) { |
116 | NewUnpackedHeader->Checksum = |
117 | computeHeaderChecksum(Cookie, Ptr, Header: NewUnpackedHeader); |
118 | PackedHeader = bit_cast<PackedHeader>(S: *NewUnpackedHeader); |
119 | atomic_store_relaxed(A: getAtomicHeader(Ptr), V: NewPackedHeader); |
120 | } |
121 | |
122 | inline void (u32 Cookie, const void *Ptr, |
123 | UnpackedHeader *) { |
124 | PackedHeader = atomic_load_relaxed(A: getConstAtomicHeader(Ptr)); |
125 | *NewUnpackedHeader = bit_cast<UnpackedHeader>(S: NewPackedHeader); |
126 | if (UNLIKELY(NewUnpackedHeader->Checksum != |
127 | computeHeaderChecksum(Cookie, Ptr, NewUnpackedHeader))) |
128 | reportHeaderCorruption(Ptr: const_cast<void *>(Ptr)); |
129 | } |
130 | |
131 | inline bool (u32 Cookie, const void *Ptr, |
132 | UnpackedHeader *) { |
133 | PackedHeader = atomic_load_relaxed(A: getConstAtomicHeader(Ptr)); |
134 | *NewUnpackedHeader = bit_cast<UnpackedHeader>(S: NewPackedHeader); |
135 | return NewUnpackedHeader->Checksum == |
136 | computeHeaderChecksum(Cookie, Ptr, Header: NewUnpackedHeader); |
137 | } |
138 | |
139 | } // namespace Chunk |
140 | |
141 | } // namespace scudo |
142 | |
143 | #endif // SCUDO_CHUNK_H_ |
144 | |