chunk.h 5.9 KB

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