Commit 5595249e authored by Kostya Kortchinsky's avatar Kostya Kortchinsky
Browse files

[scudo][standalone] Add chunk ownership function

Summary:
In order to be compliant with tcmalloc's extension ownership
determination function, we have to expose a function that will
say if a chunk was allocated by us.

As to whether or not this has security consequences: someone
able to call this function repeatedly could use it to determine
secrets (cookie) or craft a valid header. So this should not be
exposed directly to untrusted user input.

Add related tests.

Additionally clang-format caught a few things to change.

Reviewers: hctim, pcc, cferris, eugenis, vitalybuka

Subscribers: JDevlieghere, jfb, #sanitizers, llvm-commits

Tags: #sanitizers, #llvm

Differential Revision: https://reviews.llvm.org/D70908
parent a315519c
Loading
Loading
Loading
Loading
+5 −7
Original line number Diff line number Diff line
@@ -91,8 +91,7 @@ inline AtomicPackedHeader *getAtomicHeader(void *Ptr) {
                                                getHeaderSize());
}

inline
const AtomicPackedHeader *getConstAtomicHeader(const void *Ptr) {
inline const AtomicPackedHeader *getConstAtomicHeader(const void *Ptr) {
  return reinterpret_cast<const AtomicPackedHeader *>(
      reinterpret_cast<uptr>(Ptr) - getHeaderSize());
}
@@ -118,8 +117,7 @@ inline void storeHeader(u32 Cookie, void *Ptr,
  atomic_store_relaxed(getAtomicHeader(Ptr), NewPackedHeader);
}

inline
void loadHeader(u32 Cookie, const void *Ptr,
inline void loadHeader(u32 Cookie, const void *Ptr,
                       UnpackedHeader *NewUnpackedHeader) {
  PackedHeader NewPackedHeader = atomic_load_relaxed(getConstAtomicHeader(Ptr));
  *NewUnpackedHeader = bit_cast<UnpackedHeader>(NewPackedHeader);
@@ -141,8 +139,8 @@ inline void compareExchangeHeader(u32 Cookie, void *Ptr,
    reportHeaderRace(Ptr);
}

inline
bool isValid(u32 Cookie, const void *Ptr, UnpackedHeader *NewUnpackedHeader) {
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 ==
+15 −0
Original line number Diff line number Diff line
@@ -457,6 +457,18 @@ public:
    Stats.get(S);
  }

  // Returns true if the pointer provided was allocated by the current
  // allocator instance, which is compliant with tcmalloc's ownership concept.
  // A corrupted chunk will not be reported as owned, which is WAI.
  bool isOwned(const void *Ptr) {
    initThreadMaybe();
    if (!Ptr || !isAligned(reinterpret_cast<uptr>(Ptr), MinAlignment))
      return false;
    Chunk::UnpackedHeader Header;
    return Chunk::isValid(Cookie, Ptr, &Header) &&
           Header.State == Chunk::State::Allocated;
  }

private:
  using SecondaryT = typename Params::Secondary;
  typedef typename PrimaryT::SizeClassMap SizeClassMap;
@@ -468,6 +480,9 @@ private:
  static const uptr MaxAllowedMallocSize =
      FIRST_32_SECOND_64(1UL << 31, 1ULL << 40);

  static_assert(MinAlignment >= sizeof(Chunk::PackedHeader),
                "Minimal alignment must at least cover a chunk header.");

  // Constants used by the chunk iteration mechanism.
  static const u32 BlockMarker = 0x44554353U;
  static const uptr InvalidChunk = ~static_cast<uptr>(0);
+7 −0
Original line number Diff line number Diff line
@@ -32,6 +32,12 @@ template <class Config> static void testAllocator() {
                                                           Deleter);
  Allocator->reset();

  EXPECT_FALSE(Allocator->isOwned(&Mutex));
  EXPECT_FALSE(Allocator->isOwned(&Allocator));
  scudo::u64 StackVariable = 0x42424242U;
  EXPECT_FALSE(Allocator->isOwned(&StackVariable));
  EXPECT_EQ(StackVariable, 0x42424242U);

  constexpr scudo::uptr MinAlignLog = FIRST_32_SECOND_64(3U, 4U);

  // This allocates and deallocates a bunch of chunks, with a wide range of
@@ -46,6 +52,7 @@ template <class Config> static void testAllocator() {
        const scudo::uptr Size = (1U << SizeLog) + Delta;
        void *P = Allocator->allocate(Size, Origin, Align);
        EXPECT_NE(P, nullptr);
        EXPECT_TRUE(Allocator->isOwned(P));
        EXPECT_TRUE(scudo::isAligned(reinterpret_cast<scudo::uptr>(P), Align));
        EXPECT_LE(Size, Allocator->getUsableSize(P));
        memset(P, 0xaa, Size);