chunk.h 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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__) ||
  40. // defined(__ARM_FEATURE_CRC32)
  41. }
  42. namespace Chunk {
  43. // Note that in an ideal world, `State` and `Origin` should be `enum class`, and
  44. // the associated `UnpackedHeader` fields of their respective enum class type
  45. // but https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414 prevents it from
  46. // happening, as it will error, complaining the number of bits is not enough.
  47. enum Origin : u8 {
  48. Malloc = 0,
  49. New = 1,
  50. NewArray = 2,
  51. Memalign = 3,
  52. };
  53. enum State : u8 { Available = 0, Allocated = 1, Quarantined = 2 };
  54. typedef u64 PackedHeader;
  55. // Update the 'Mask' constants to reflect changes in this structure.
  56. struct UnpackedHeader {
  57. uptr ClassId : 8;
  58. u8 State : 2;
  59. // Origin if State == Allocated, or WasZeroed otherwise.
  60. u8 OriginOrWasZeroed : 2;
  61. uptr SizeOrUnusedBytes : 20;
  62. uptr Offset : 16;
  63. uptr Checksum : 16;
  64. };
  65. typedef atomic_u64 AtomicPackedHeader;
  66. static_assert(sizeof(UnpackedHeader) == sizeof(PackedHeader), "");
  67. // Those constants are required to silence some -Werror=conversion errors when
  68. // assigning values to the related bitfield variables.
  69. constexpr uptr ClassIdMask = (1UL << 8) - 1;
  70. constexpr u8 StateMask = (1U << 2) - 1;
  71. constexpr u8 OriginMask = (1U << 2) - 1;
  72. constexpr uptr SizeOrUnusedBytesMask = (1UL << 20) - 1;
  73. constexpr uptr OffsetMask = (1UL << 16) - 1;
  74. constexpr uptr ChecksumMask = (1UL << 16) - 1;
  75. constexpr uptr getHeaderSize() {
  76. return roundUpTo(sizeof(PackedHeader), 1U << SCUDO_MIN_ALIGNMENT_LOG);
  77. }
  78. inline AtomicPackedHeader *getAtomicHeader(void *Ptr) {
  79. return reinterpret_cast<AtomicPackedHeader *>(reinterpret_cast<uptr>(Ptr) -
  80. getHeaderSize());
  81. }
  82. inline const AtomicPackedHeader *getConstAtomicHeader(const void *Ptr) {
  83. return reinterpret_cast<const AtomicPackedHeader *>(
  84. reinterpret_cast<uptr>(Ptr) - getHeaderSize());
  85. }
  86. // We do not need a cryptographically strong hash for the checksum, but a CRC
  87. // type function that can alert us in the event a header is invalid or
  88. // corrupted. Ideally slightly better than a simple xor of all fields.
  89. static inline u16 computeHeaderChecksum(u32 Cookie, const void *Ptr,
  90. UnpackedHeader *Header) {
  91. UnpackedHeader ZeroChecksumHeader = *Header;
  92. ZeroChecksumHeader.Checksum = 0;
  93. uptr HeaderHolder[sizeof(UnpackedHeader) / sizeof(uptr)];
  94. memcpy(&HeaderHolder, &ZeroChecksumHeader, sizeof(HeaderHolder));
  95. return computeChecksum(Cookie, reinterpret_cast<uptr>(Ptr), HeaderHolder,
  96. ARRAY_SIZE(HeaderHolder));
  97. }
  98. inline void storeHeader(u32 Cookie, void *Ptr,
  99. UnpackedHeader *NewUnpackedHeader) {
  100. NewUnpackedHeader->Checksum =
  101. computeHeaderChecksum(Cookie, Ptr, NewUnpackedHeader);
  102. PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader);
  103. atomic_store_relaxed(getAtomicHeader(Ptr), NewPackedHeader);
  104. }
  105. inline void loadHeader(u32 Cookie, const void *Ptr,
  106. UnpackedHeader *NewUnpackedHeader) {
  107. PackedHeader NewPackedHeader = atomic_load_relaxed(getConstAtomicHeader(Ptr));
  108. *NewUnpackedHeader = bit_cast<UnpackedHeader>(NewPackedHeader);
  109. if (UNLIKELY(NewUnpackedHeader->Checksum !=
  110. computeHeaderChecksum(Cookie, Ptr, NewUnpackedHeader)))
  111. reportHeaderCorruption(const_cast<void *>(Ptr));
  112. }
  113. inline void compareExchangeHeader(u32 Cookie, void *Ptr,
  114. UnpackedHeader *NewUnpackedHeader,
  115. UnpackedHeader *OldUnpackedHeader) {
  116. NewUnpackedHeader->Checksum =
  117. computeHeaderChecksum(Cookie, Ptr, NewUnpackedHeader);
  118. PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader);
  119. PackedHeader OldPackedHeader = bit_cast<PackedHeader>(*OldUnpackedHeader);
  120. if (UNLIKELY(!atomic_compare_exchange_strong(
  121. getAtomicHeader(Ptr), &OldPackedHeader, NewPackedHeader,
  122. memory_order_relaxed)))
  123. reportHeaderRace(Ptr);
  124. }
  125. inline bool isValid(u32 Cookie, const void *Ptr,
  126. UnpackedHeader *NewUnpackedHeader) {
  127. PackedHeader NewPackedHeader = atomic_load_relaxed(getConstAtomicHeader(Ptr));
  128. *NewUnpackedHeader = bit_cast<UnpackedHeader>(NewPackedHeader);
  129. return NewUnpackedHeader->Checksum ==
  130. computeHeaderChecksum(Cookie, Ptr, NewUnpackedHeader);
  131. }
  132. } // namespace Chunk
  133. } // namespace scudo
  134. #endif // SCUDO_CHUNK_H_