aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/compiler-rt/lib/scudo/standalone/chunk.h
blob: 69b8e1b12a91c091a74bd8715f0568a761507659 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//===-- chunk.h -------------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef SCUDO_CHUNK_H_
#define SCUDO_CHUNK_H_

#include "platform.h"

#include "atomic_helpers.h"
#include "checksum.h"
#include "common.h"
#include "report.h"

namespace scudo {

extern Checksum HashAlgorithm;

inline u16 computeChecksum(u32 Seed, uptr Value, uptr *Array, uptr ArraySize) {
  // If the hardware CRC32 feature is defined here, it was enabled everywhere,
  // as opposed to only for crc32_hw.cpp. This means that other hardware
  // specific instructions were likely emitted at other places, and as a result
  // there is no reason to not use it here.
#if defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32)
  u32 Crc = static_cast<u32>(CRC32_INTRINSIC(Seed, Value));
  for (uptr I = 0; I < ArraySize; I++)
    Crc = static_cast<u32>(CRC32_INTRINSIC(Crc, Array[I]));
  return static_cast<u16>(Crc ^ (Crc >> 16));
#else
  if (HashAlgorithm == Checksum::HardwareCRC32) {
    u32 Crc = computeHardwareCRC32(Seed, Value);
    for (uptr I = 0; I < ArraySize; I++)
      Crc = computeHardwareCRC32(Crc, Array[I]);
    return static_cast<u16>(Crc ^ (Crc >> 16));
  } else {
    u16 Checksum = computeBSDChecksum(static_cast<u16>(Seed), Value);
    for (uptr I = 0; I < ArraySize; I++)
      Checksum = computeBSDChecksum(Checksum, Array[I]);
    return Checksum;
  }
#endif // defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32)
}

namespace Chunk {

// Note that in an ideal world, `State` and `Origin` should be `enum class`, and
// the associated `UnpackedHeader` fields of their respective enum class type
// but https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414 prevents it from
// happening, as it will error, complaining the number of bits is not enough.
enum Origin : u8 {
  Malloc = 0,
  New = 1,
  NewArray = 2,
  Memalign = 3,
};

enum State : u8 { Available = 0, Allocated = 1, Quarantined = 2 };

typedef u64 PackedHeader;
// Update the 'Mask' constants to reflect changes in this structure.
struct UnpackedHeader {
  uptr ClassId : 8;
  u8 State : 2;
  // Origin if State == Allocated, or WasZeroed otherwise.
  u8 OriginOrWasZeroed : 2;
  uptr SizeOrUnusedBytes : 20;
  uptr Offset : 16;
  uptr Checksum : 16;
};
typedef atomic_u64 AtomicPackedHeader;
static_assert(sizeof(UnpackedHeader) == sizeof(PackedHeader), "");

// Those constants are required to silence some -Werror=conversion errors when
// assigning values to the related bitfield variables.
constexpr uptr ClassIdMask = (1UL << 8) - 1;
constexpr u8 StateMask = (1U << 2) - 1;
constexpr u8 OriginMask = (1U << 2) - 1;
constexpr uptr SizeOrUnusedBytesMask = (1UL << 20) - 1;
constexpr uptr OffsetMask = (1UL << 16) - 1;
constexpr uptr ChecksumMask = (1UL << 16) - 1;

constexpr uptr getHeaderSize() {
  return roundUpTo(sizeof(PackedHeader), 1U << SCUDO_MIN_ALIGNMENT_LOG);
}

inline AtomicPackedHeader *getAtomicHeader(void *Ptr) {
  return reinterpret_cast<AtomicPackedHeader *>(reinterpret_cast<uptr>(Ptr) -
                                                getHeaderSize());
}

inline const AtomicPackedHeader *getConstAtomicHeader(const void *Ptr) {
  return reinterpret_cast<const AtomicPackedHeader *>(
      reinterpret_cast<uptr>(Ptr) - getHeaderSize());
}

// We do not need a cryptographically strong hash for the checksum, but a CRC
// type function that can alert us in the event a header is invalid or
// corrupted. Ideally slightly better than a simple xor of all fields.
static inline u16 computeHeaderChecksum(u32 Cookie, const void *Ptr,
                                        UnpackedHeader *Header) {
  UnpackedHeader ZeroChecksumHeader = *Header;
  ZeroChecksumHeader.Checksum = 0;
  uptr HeaderHolder[sizeof(UnpackedHeader) / sizeof(uptr)];
  memcpy(&HeaderHolder, &ZeroChecksumHeader, sizeof(HeaderHolder));
  return computeChecksum(Cookie, reinterpret_cast<uptr>(Ptr), HeaderHolder,
                         ARRAY_SIZE(HeaderHolder));
}

inline void storeHeader(u32 Cookie, void *Ptr,
                        UnpackedHeader *NewUnpackedHeader) {
  NewUnpackedHeader->Checksum =
      computeHeaderChecksum(Cookie, Ptr, NewUnpackedHeader);
  PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader);
  atomic_store_relaxed(getAtomicHeader(Ptr), NewPackedHeader);
}

inline void loadHeader(u32 Cookie, const void *Ptr,
                       UnpackedHeader *NewUnpackedHeader) {
  PackedHeader NewPackedHeader = atomic_load_relaxed(getConstAtomicHeader(Ptr));
  *NewUnpackedHeader = bit_cast<UnpackedHeader>(NewPackedHeader);
  if (UNLIKELY(NewUnpackedHeader->Checksum !=
               computeHeaderChecksum(Cookie, Ptr, NewUnpackedHeader)))
    reportHeaderCorruption(const_cast<void *>(Ptr));
}

inline void compareExchangeHeader(u32 Cookie, void *Ptr,
                                  UnpackedHeader *NewUnpackedHeader,
                                  UnpackedHeader *OldUnpackedHeader) {
  NewUnpackedHeader->Checksum =
      computeHeaderChecksum(Cookie, Ptr, NewUnpackedHeader);
  PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader);
  PackedHeader OldPackedHeader = bit_cast<PackedHeader>(*OldUnpackedHeader);
  if (UNLIKELY(!atomic_compare_exchange_strong(
          getAtomicHeader(Ptr), &OldPackedHeader, NewPackedHeader,
          memory_order_relaxed)))
    reportHeaderRace(Ptr);
}

inline bool isValid(u32 Cookie, const void *Ptr,
                    UnpackedHeader *NewUnpackedHeader) {
  PackedHeader NewPackedHeader = atomic_load_relaxed(getConstAtomicHeader(Ptr));
  *NewUnpackedHeader = bit_cast<UnpackedHeader>(NewPackedHeader);
  return NewUnpackedHeader->Checksum ==
         computeHeaderChecksum(Cookie, Ptr, NewUnpackedHeader);
}

} // namespace Chunk

} // namespace scudo

#endif // SCUDO_CHUNK_H_