• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <cstdint>
6 #include <string>
7 #include <vector>
8 
9 #include "build/build_config.h"
10 #include "partition_alloc/partition_alloc_config.h"
11 #include "partition_alloc/partition_freelist_entry.h"
12 #include "partition_alloc/partition_page.h"
13 #include "partition_alloc/partition_root.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 
16 // With *SAN, PartitionAlloc is rerouted to malloc().
17 #if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
18 
19 namespace partition_alloc::internal {
20 namespace {
21 
22 // Death tests misbehave on Android, crbug.com/1240184
23 #if !BUILDFLAG(IS_ANDROID) && defined(GTEST_HAS_DEATH_TEST) && \
24     PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY)
25 
TEST(HardeningTest,PartialCorruption)26 TEST(HardeningTest, PartialCorruption) {
27   std::string important_data("very important");
28   char* to_corrupt = const_cast<char*>(important_data.c_str());
29 
30   PartitionOptions opts;
31   opts.aligned_alloc = PartitionOptions::kAllowed;
32   PartitionRoot root(opts);
33   root.UncapEmptySlotSpanMemoryForTesting();
34 
35   const size_t kAllocSize = 100;
36   void* data = root.Alloc(kAllocSize);
37   void* data2 = root.Alloc(kAllocSize);
38   root.Free(data2);
39   root.Free(data);
40 
41   // root->bucket->active_slot_span_head->freelist_head points to data, next_
42   // points to data2. We can corrupt *data to get overwrite the next_ pointer.
43   // Even if it looks reasonable (valid encoded pointer), freelist corruption
44   // detection will make the code crash, because shadow_ doesn't match
45   // encoded_next_.
46   EncodedNextFreelistEntry::EmplaceAndInitForTest(root.ObjectToSlotStart(data),
47                                                   to_corrupt, false);
48   EXPECT_DEATH(root.Alloc(kAllocSize), "");
49 }
50 
TEST(HardeningTest,OffHeapPointerCrashing)51 TEST(HardeningTest, OffHeapPointerCrashing) {
52   std::string important_data("very important");
53   char* to_corrupt = const_cast<char*>(important_data.c_str());
54 
55   PartitionOptions opts;
56   opts.aligned_alloc = PartitionOptions::kAllowed;
57   PartitionRoot root(opts);
58   root.UncapEmptySlotSpanMemoryForTesting();
59 
60   const size_t kAllocSize = 100;
61   void* data = root.Alloc(kAllocSize);
62   void* data2 = root.Alloc(kAllocSize);
63   root.Free(data2);
64   root.Free(data);
65 
66   // See "PartialCorruption" above for details. This time, make shadow_
67   // consistent.
68   EncodedNextFreelistEntry::EmplaceAndInitForTest(root.ObjectToSlotStart(data),
69                                                   to_corrupt, true);
70 
71   // Crashes, because |to_corrupt| is not on the same superpage as data.
72   EXPECT_DEATH(root.Alloc(kAllocSize), "");
73 }
74 
TEST(HardeningTest,MetadataPointerCrashing)75 TEST(HardeningTest, MetadataPointerCrashing) {
76   PartitionOptions opts;
77   opts.aligned_alloc = PartitionOptions::kAllowed;
78   PartitionRoot root(opts);
79   root.UncapEmptySlotSpanMemoryForTesting();
80 
81   const size_t kAllocSize = 100;
82   void* data = root.Alloc(kAllocSize);
83   void* data2 = root.Alloc(kAllocSize);
84   root.Free(data2);
85   root.Free(data);
86 
87   uintptr_t slot_start = root.ObjectToSlotStart(data);
88   auto* metadata = SlotSpanMetadata::FromSlotStart(slot_start);
89   EncodedNextFreelistEntry::EmplaceAndInitForTest(slot_start, metadata, true);
90 
91   // Crashes, because |metadata| points inside the metadata area.
92   EXPECT_DEATH(root.Alloc(kAllocSize), "");
93 }
94 #endif  // !BUILDFLAG(IS_ANDROID) && defined(GTEST_HAS_DEATH_TEST) &&
95         // PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY)
96 
97 // Below test also misbehaves on Android; as above, death tests don't
98 // quite work (crbug.com/1240184), and having free slot bitmaps enabled
99 // force the expectations below to crash.
100 #if !BUILDFLAG(IS_ANDROID)
101 
TEST(HardeningTest,SuccessfulCorruption)102 TEST(HardeningTest, SuccessfulCorruption) {
103   PartitionOptions opts;
104   opts.aligned_alloc = PartitionOptions::kAllowed;
105   PartitionRoot root(opts);
106   root.UncapEmptySlotSpanMemoryForTesting();
107 
108   uintptr_t* zero_vector = reinterpret_cast<uintptr_t*>(
109       root.Alloc<AllocFlags::kZeroFill>(100 * sizeof(uintptr_t), ""));
110   ASSERT_TRUE(zero_vector);
111   // Pointer to the middle of an existing allocation.
112   uintptr_t* to_corrupt = zero_vector + 20;
113 
114   const size_t kAllocSize = 100;
115   void* data = root.Alloc(kAllocSize);
116   void* data2 = root.Alloc(kAllocSize);
117   root.Free(data2);
118   root.Free(data);
119 
120   EncodedNextFreelistEntry::EmplaceAndInitForTest(root.ObjectToSlotStart(data),
121                                                   to_corrupt, true);
122 
123 #if BUILDFLAG(USE_FREESLOT_BITMAP)
124   // This part crashes with freeslot bitmap because it detects freelist
125   // corruptions, which is rather desirable behavior.
126   EXPECT_DEATH_IF_SUPPORTED(root.Alloc(kAllocSize), "");
127 #else
128   // Next allocation is what was in
129   // root->bucket->active_slot_span_head->freelist_head, so not the corrupted
130   // pointer.
131   void* new_data = root.Alloc(kAllocSize);
132   ASSERT_EQ(new_data, data);
133 
134   // Not crashing, because a zeroed area is a "valid" freelist entry.
135   void* new_data2 = root.Alloc(kAllocSize);
136   // Now we have a pointer to the middle of an existing allocation.
137   EXPECT_EQ(new_data2, to_corrupt);
138 #endif  // BUILDFLAG(USE_FREESLOT_BITMAP)
139 }
140 #endif  // !BUILDFLAG(IS_ANDROID)
141 
142 }  // namespace
143 }  // namespace partition_alloc::internal
144 
145 #endif  // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
146