[compiler-rt] r360290 - [scudo][standalone] Introduce the chunk header

Kostya Kortchinsky via llvm-commits llvm-commits at lists.llvm.org
Wed May 8 14:54:02 PDT 2019


Author: cryptoad
Date: Wed May  8 14:54:02 2019
New Revision: 360290

URL: http://llvm.org/viewvc/llvm-project?rev=360290&view=rev
Log:
[scudo][standalone] Introduce the chunk header

Summary:
... and its related functions.

The structure and its functionalities are identical to existing ones.
The header stores information on a `scudo::Chunk` to be able to detect
inconsitencies or potential corruption attempts. It is checksummed for
that purpose.

Reviewers: morehouse, eugenis, vitalybuka, hctim

Reviewed By: vitalybuka

Subscribers: mgorny, delcypher, jfb, #sanitizers, llvm-commits

Tags: #llvm, #sanitizers

Differential Revision: https://reviews.llvm.org/D61654

Added:
    compiler-rt/trunk/lib/scudo/standalone/chunk.h
    compiler-rt/trunk/lib/scudo/standalone/tests/chunk_test.cc
Modified:
    compiler-rt/trunk/lib/scudo/standalone/CMakeLists.txt
    compiler-rt/trunk/lib/scudo/standalone/checksum.cc
    compiler-rt/trunk/lib/scudo/standalone/checksum.h
    compiler-rt/trunk/lib/scudo/standalone/tests/CMakeLists.txt

Modified: compiler-rt/trunk/lib/scudo/standalone/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/scudo/standalone/CMakeLists.txt?rev=360290&r1=360289&r2=360290&view=diff
==============================================================================
--- compiler-rt/trunk/lib/scudo/standalone/CMakeLists.txt (original)
+++ compiler-rt/trunk/lib/scudo/standalone/CMakeLists.txt Wed May  8 14:54:02 2019
@@ -60,6 +60,7 @@ set(SCUDO_HEADERS
   atomic_helpers.h
   bytemap.h
   checksum.h
+  chunk.h
   flags.h
   flags_parser.h
   fuchsia.h

Modified: compiler-rt/trunk/lib/scudo/standalone/checksum.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/scudo/standalone/checksum.cc?rev=360290&r1=360289&r2=360290&view=diff
==============================================================================
--- compiler-rt/trunk/lib/scudo/standalone/checksum.cc (original)
+++ compiler-rt/trunk/lib/scudo/standalone/checksum.cc Wed May  8 14:54:02 2019
@@ -22,7 +22,7 @@
 
 namespace scudo {
 
-atomic_u8 HashAlgorithm = {BSDChecksum};
+Checksum HashAlgorithm = {Checksum::BSD};
 
 #if defined(__x86_64__) || defined(__i386__)
 // i386 and x86_64 specific code to detect CRC32 hardware support via CPUID.

Modified: compiler-rt/trunk/lib/scudo/standalone/checksum.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/scudo/standalone/checksum.h?rev=360290&r1=360289&r2=360290&view=diff
==============================================================================
--- compiler-rt/trunk/lib/scudo/standalone/checksum.h (original)
+++ compiler-rt/trunk/lib/scudo/standalone/checksum.h Wed May  8 14:54:02 2019
@@ -28,8 +28,8 @@
 
 namespace scudo {
 
-enum ChecksumType : u8 {
-  BSDChecksum = 0,
+enum class Checksum : u8 {
+  BSD = 0,
   HardwareCRC32 = 1,
 };
 

Added: compiler-rt/trunk/lib/scudo/standalone/chunk.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/scudo/standalone/chunk.h?rev=360290&view=auto
==============================================================================
--- compiler-rt/trunk/lib/scudo/standalone/chunk.h (added)
+++ compiler-rt/trunk/lib/scudo/standalone/chunk.h Wed May  8 14:54:02 2019
@@ -0,0 +1,162 @@
+//===-- 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.cc. 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 & 0xffff) ^ (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 & 0xffff) ^ (Crc >> 16));
+  } else {
+    u16 Checksum = computeBSDChecksum(static_cast<u16>(Seed & 0xffff), 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 {
+  u64 Checksum : 16;
+  u64 ClassId : 8;
+  u64 SizeOrUnusedBytes : 20;
+  u8 State : 2;
+  u8 Origin : 2;
+  u64 Offset : 16;
+};
+typedef atomic_u64 AtomicPackedHeader;
+COMPILER_CHECK(sizeof(UnpackedHeader) == sizeof(PackedHeader));
+
+// Those constants are required to silence some -Werror=conversion errors when
+// assigning values to the related bitfield variables.
+constexpr uptr ChecksumMask = (1UL << 16) - 1;
+constexpr uptr ClassIdMask = (1UL << 8) - 1;
+constexpr uptr SizeOrUnusedBytesMask = (1UL << 20) - 1;
+constexpr uptr StateMask = (1UL << 2) - 1;
+constexpr uptr OriginMask = (1UL << 2) - 1;
+constexpr uptr OffsetMask = (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());
+}
+
+INLINE void *getBlockBegin(const void *Ptr, UnpackedHeader *Header) {
+  return reinterpret_cast<void *>(reinterpret_cast<uptr>(Ptr) -
+                                  getHeaderSize() -
+                                  (Header->Offset << SCUDO_MIN_ALIGNMENT_LOG));
+}
+
+// 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_

Modified: compiler-rt/trunk/lib/scudo/standalone/tests/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/scudo/standalone/tests/CMakeLists.txt?rev=360290&r1=360289&r2=360290&view=diff
==============================================================================
--- compiler-rt/trunk/lib/scudo/standalone/tests/CMakeLists.txt (original)
+++ compiler-rt/trunk/lib/scudo/standalone/tests/CMakeLists.txt Wed May  8 14:54:02 2019
@@ -52,6 +52,7 @@ set(SCUDO_UNIT_TEST_SOURCES
   atomic_test.cc
   bytemap_test.cc
   checksum_test.cc
+  chunk_test.cc
   flags_test.cc
   list_test.cc
   map_test.cc

Added: compiler-rt/trunk/lib/scudo/standalone/tests/chunk_test.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/scudo/standalone/tests/chunk_test.cc?rev=360290&view=auto
==============================================================================
--- compiler-rt/trunk/lib/scudo/standalone/tests/chunk_test.cc (added)
+++ compiler-rt/trunk/lib/scudo/standalone/tests/chunk_test.cc Wed May  8 14:54:02 2019
@@ -0,0 +1,82 @@
+//===-- chunk_test.cc -------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "chunk.h"
+
+#include "gtest/gtest.h"
+
+#include <stdlib.h>
+
+static constexpr scudo::uptr HeaderSize = scudo::Chunk::getHeaderSize();
+static constexpr scudo::u32 Cookie = 0x41424344U;
+static constexpr scudo::u32 InvalidCookie = 0x11223344U;
+
+static void initChecksum(void) {
+  if (&scudo::computeHardwareCRC32 && scudo::hasHardwareCRC32())
+    scudo::HashAlgorithm = scudo::Checksum::HardwareCRC32;
+}
+
+TEST(ScudoChunkTest, ChunkBasic) {
+  initChecksum();
+  const scudo::uptr Size = 0x100U;
+  scudo::Chunk::UnpackedHeader Header = {};
+  void *Block = malloc(HeaderSize + Size);
+  void *P = reinterpret_cast<void *>(reinterpret_cast<scudo::uptr>(Block) +
+                                     HeaderSize);
+  scudo::Chunk::storeHeader(Cookie, P, &Header);
+  memset(P, 'A', Size);
+  EXPECT_EQ(scudo::Chunk::getBlockBegin(P, &Header), Block);
+  scudo::Chunk::loadHeader(Cookie, P, &Header);
+  EXPECT_TRUE(scudo::Chunk::isValid(Cookie, P, &Header));
+  EXPECT_FALSE(scudo::Chunk::isValid(InvalidCookie, P, &Header));
+  EXPECT_DEATH(scudo::Chunk::loadHeader(InvalidCookie, P, &Header), "");
+  free(Block);
+}
+
+TEST(ScudoChunkTest, ChunkCmpXchg) {
+  initChecksum();
+  const scudo::uptr Size = 0x100U;
+  scudo::Chunk::UnpackedHeader OldHeader = {};
+  OldHeader.Origin = scudo::Chunk::Origin::Malloc;
+  OldHeader.ClassId = 0x42U;
+  OldHeader.SizeOrUnusedBytes = Size;
+  OldHeader.State = scudo::Chunk::State::Allocated;
+  void *Block = malloc(HeaderSize + Size);
+  void *P = reinterpret_cast<void *>(reinterpret_cast<scudo::uptr>(Block) +
+                                     HeaderSize);
+  scudo::Chunk::storeHeader(Cookie, P, &OldHeader);
+  memset(P, 'A', Size);
+  scudo::Chunk::UnpackedHeader NewHeader = OldHeader;
+  NewHeader.State = scudo::Chunk::State::Quarantined;
+  scudo::Chunk::compareExchangeHeader(Cookie, P, &NewHeader, &OldHeader);
+  NewHeader = {};
+  EXPECT_TRUE(scudo::Chunk::isValid(Cookie, P, &NewHeader));
+  EXPECT_EQ(NewHeader.State, scudo::Chunk::State::Quarantined);
+  EXPECT_FALSE(scudo::Chunk::isValid(InvalidCookie, P, &NewHeader));
+  free(Block);
+}
+
+TEST(ScudoChunkTest, CorruptHeader) {
+  initChecksum();
+  const scudo::uptr Size = 0x100U;
+  scudo::Chunk::UnpackedHeader Header = {};
+  void *Block = malloc(HeaderSize + Size);
+  void *P = reinterpret_cast<void *>(reinterpret_cast<scudo::uptr>(Block) +
+                                     HeaderSize);
+  scudo::Chunk::storeHeader(Cookie, P, &Header);
+  memset(P, 'A', Size);
+  EXPECT_EQ(scudo::Chunk::getBlockBegin(P, &Header), Block);
+  scudo::Chunk::loadHeader(Cookie, P, &Header);
+  // Simulate a couple of corrupted bits per byte of header data.
+  for (scudo::uptr I = 0; I < sizeof(scudo::Chunk::PackedHeader); I++) {
+    *(reinterpret_cast<scudo::u8 *>(Block) + I) ^= 0x42U;
+    EXPECT_DEATH(scudo::Chunk::loadHeader(Cookie, P, &Header), "");
+    *(reinterpret_cast<scudo::u8 *>(Block) + I) ^= 0x42U;
+  }
+  free(Block);
+}




More information about the llvm-commits mailing list