• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- scudo_hooks_test.cpp ------------------------------------*- 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 
9 #include "tests/scudo_unit_test.h"
10 
11 #include "allocator_config.h"
12 #include "combined.h"
13 
14 namespace {
15 void *LastAllocatedPtr = nullptr;
16 size_t LastRequestSize = 0;
17 void *LastDeallocatedPtr = nullptr;
18 } // namespace
19 
20 // Scudo defines weak symbols that can be defined by a client binary
21 // to register callbacks at key points in the allocation timeline.  In
22 // order to enforce those invariants, we provide definitions that
23 // update some global state every time they are called, so that tests
24 // can inspect their effects.  An unfortunate side effect of this
25 // setup is that because those symbols are part of the binary, they
26 // can't be selectively enabled; that means that they will get called
27 // on unrelated tests in the same compilation unit. To mitigate this
28 // issue, we insulate those tests in a separate compilation unit.
29 extern "C" {
__scudo_allocate_hook(void * Ptr,size_t Size)30 __attribute__((visibility("default"))) void __scudo_allocate_hook(void *Ptr,
31                                                                   size_t Size) {
32   LastAllocatedPtr = Ptr;
33   LastRequestSize = Size;
34 }
__scudo_deallocate_hook(void * Ptr)35 __attribute__((visibility("default"))) void __scudo_deallocate_hook(void *Ptr) {
36   LastDeallocatedPtr = Ptr;
37 }
38 }
39 
40 // Simple check that allocation callbacks, when registered, are called:
41 //   1) __scudo_allocate_hook is called when allocating.
42 //   2) __scudo_deallocate_hook is called when deallocating.
43 //   3) Both hooks are called when reallocating.
44 //   4) Neither are called for a no-op reallocation.
TEST(ScudoHooksTest,AllocateHooks)45 TEST(ScudoHooksTest, AllocateHooks) {
46   scudo::Allocator<scudo::DefaultConfig> Allocator;
47   constexpr scudo::uptr DefaultSize = 16U;
48   constexpr scudo::Chunk::Origin Origin = scudo::Chunk::Origin::Malloc;
49 
50   // Simple allocation and deallocation.
51   {
52     LastAllocatedPtr = nullptr;
53     LastRequestSize = 0;
54 
55     void *Ptr = Allocator.allocate(DefaultSize, Origin);
56 
57     EXPECT_EQ(Ptr, LastAllocatedPtr);
58     EXPECT_EQ(DefaultSize, LastRequestSize);
59 
60     LastDeallocatedPtr = nullptr;
61 
62     Allocator.deallocate(Ptr, Origin);
63 
64     EXPECT_EQ(Ptr, LastDeallocatedPtr);
65   }
66 
67   // Simple no-op, same size reallocation.
68   {
69     void *Ptr = Allocator.allocate(DefaultSize, Origin);
70 
71     LastAllocatedPtr = nullptr;
72     LastRequestSize = 0;
73     LastDeallocatedPtr = nullptr;
74 
75     void *NewPtr = Allocator.reallocate(Ptr, DefaultSize);
76 
77     EXPECT_EQ(Ptr, NewPtr);
78     EXPECT_EQ(nullptr, LastAllocatedPtr);
79     EXPECT_EQ(0U, LastRequestSize);
80     EXPECT_EQ(nullptr, LastDeallocatedPtr);
81   }
82 
83   // Reallocation in increasing size classes. This ensures that at
84   // least one of the reallocations will be meaningful.
85   {
86     void *Ptr = Allocator.allocate(0, Origin);
87 
88     for (scudo::uptr ClassId = 1U;
89          ClassId <= scudo::DefaultConfig::Primary::SizeClassMap::LargestClassId;
90          ++ClassId) {
91       const scudo::uptr Size =
92           scudo::DefaultConfig::Primary::SizeClassMap::getSizeByClassId(
93               ClassId);
94 
95       LastAllocatedPtr = nullptr;
96       LastRequestSize = 0;
97       LastDeallocatedPtr = nullptr;
98 
99       void *NewPtr = Allocator.reallocate(Ptr, Size);
100 
101       if (NewPtr != Ptr) {
102         EXPECT_EQ(NewPtr, LastAllocatedPtr);
103         EXPECT_EQ(Size, LastRequestSize);
104         EXPECT_EQ(Ptr, LastDeallocatedPtr);
105       } else {
106         EXPECT_EQ(nullptr, LastAllocatedPtr);
107         EXPECT_EQ(0U, LastRequestSize);
108         EXPECT_EQ(nullptr, LastDeallocatedPtr);
109       }
110 
111       Ptr = NewPtr;
112     }
113   }
114 }
115