• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "perfetto/base/paged_memory.h"
18 
19 #include <stdint.h>
20 
21 #include "gtest/gtest.h"
22 #include "perfetto/base/build_config.h"
23 #include "src/base/test/vm_test_utils.h"
24 
25 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX) && \
26     !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&    \
27     !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
28 #include <sys/resource.h>
29 #endif
30 
31 namespace perfetto {
32 namespace base {
33 namespace {
34 
TEST(PagedMemoryTest,Basic)35 TEST(PagedMemoryTest, Basic) {
36   const size_t kNumPages = 10;
37   const size_t kSize = 4096 * kNumPages;
38   void* ptr_raw = nullptr;
39   {
40     PagedMemory mem = PagedMemory::Allocate(kSize);
41     ASSERT_TRUE(mem.IsValid());
42     ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(mem.Get()) % 4096);
43     ptr_raw = mem.Get();
44     for (size_t i = 0; i < kSize / sizeof(uint64_t); i++)
45       ASSERT_EQ(0u, *(reinterpret_cast<uint64_t*>(mem.Get()) + i));
46 
47 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
48     ASSERT_TRUE(vm_test_utils::IsMapped(ptr_raw, kSize));
49 #endif
50 
51 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
52     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
53     ASSERT_TRUE(mem.AdviseDontNeed(ptr_raw, kSize));
54 
55     // Make sure the pages were removed from the working set.
56     ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw, kSize));
57 #endif
58   }
59 
60 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
61   // Freed memory is necessarily not mapped in to the process.
62   ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw, kSize));
63 #endif
64 }
65 
TEST(PagedMemoryTest,Uncommitted)66 TEST(PagedMemoryTest, Uncommitted) {
67   constexpr size_t kNumPages = 4096;
68   constexpr size_t kSize = 4096 * kNumPages;
69   char* ptr_raw = nullptr;
70   {
71     PagedMemory mem = PagedMemory::Allocate(kSize, PagedMemory::kDontCommit);
72     ASSERT_TRUE(mem.IsValid());
73     ptr_raw = reinterpret_cast<char*>(mem.Get());
74 
75 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
76     // Windows only commits the first 1024 pages.
77     constexpr size_t kMappedSize = 4096 * 1024;
78 
79     for (size_t i = 0; i < kMappedSize / sizeof(uint64_t); i++)
80       ASSERT_EQ(0u, *(reinterpret_cast<uint64_t*>(mem.Get()) + i));
81 
82     ASSERT_TRUE(vm_test_utils::IsMapped(ptr_raw, kMappedSize));
83 
84     // Next page shouldn't be mapped.
85     ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw + kMappedSize, 4096));
86     EXPECT_DEATH({ ptr_raw[kMappedSize] = 'x'; }, ".*");
87 
88     // Commit the remaining pages.
89     mem.EnsureCommitted(kSize);
90 
91     for (size_t i = kMappedSize / sizeof(uint64_t);
92          i < kSize / sizeof(uint64_t); i++) {
93       ASSERT_EQ(0u, *(reinterpret_cast<uint64_t*>(mem.Get()) + i));
94     }
95 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
96     // Fuchsia doesn't yet support paging. So this should be a no-op.
97     mem.EnsureCommitted(kSize);
98     for (size_t i = 0; i < kSize / sizeof(uint64_t); i++)
99       ASSERT_EQ(0u, *(reinterpret_cast<uint64_t*>(mem.Get()) + i));
100 #else
101     // Linux only maps on access.
102     ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw, kSize));
103 
104     // This should not have any effect.
105     mem.EnsureCommitted(kSize);
106     ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw, kSize));
107 
108     for (size_t i = 0; i < kSize / sizeof(uint64_t); i++)
109       ASSERT_EQ(0u, *(reinterpret_cast<uint64_t*>(mem.Get()) + i));
110     ASSERT_TRUE(vm_test_utils::IsMapped(ptr_raw, kSize));
111 #endif
112   }
113 
114 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
115   // Freed memory is necessarily not mapped in to the process.
116   ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw, kSize));
117 #endif
118 }
119 
120 #if defined(ADDRESS_SANITIZER)
TEST(PagedMemoryTest,AccessUncommittedMemoryTriggersASAN)121 TEST(PagedMemoryTest, AccessUncommittedMemoryTriggersASAN) {
122   EXPECT_DEATH(
123       {
124         constexpr size_t kNumPages = 4096;
125         constexpr size_t kSize = 4096 * kNumPages;
126         PagedMemory mem =
127             PagedMemory::Allocate(kSize, PagedMemory::kDontCommit);
128         ASSERT_TRUE(mem.IsValid());
129         char* ptr_raw = reinterpret_cast<char*>(mem.Get());
130         // Only the first 1024 pages are mapped.
131         constexpr size_t kMappedSize = 4096 * 1024;
132         ptr_raw[kMappedSize] = 'x';
133         abort();
134       },
135       "AddressSanitizer: .*");
136 }
137 #endif  // ADDRESS_SANITIZER
138 
TEST(PagedMemoryTest,GuardRegions)139 TEST(PagedMemoryTest, GuardRegions) {
140   const size_t kSize = 4096;
141   PagedMemory mem = PagedMemory::Allocate(kSize);
142   ASSERT_TRUE(mem.IsValid());
143   volatile char* raw = reinterpret_cast<char*>(mem.Get());
144   EXPECT_DEATH({ raw[-1] = 'x'; }, ".*");
145   EXPECT_DEATH({ raw[kSize] = 'x'; }, ".*");
146 }
147 
148 // Disable this on:
149 // MacOS: because it doesn't seem to have an equivalent rlimit to bound mmap().
150 // Fuchsia: doesn't support rlimit.
151 // Sanitizers: they seem to try to shadow mmaped memory and fail due to OOMs.
152 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX) &&                                 \
153     !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&                                    \
154     !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) && !defined(ADDRESS_SANITIZER) && \
155     !defined(LEAK_SANITIZER) && !defined(THREAD_SANITIZER) &&                  \
156     !defined(MEMORY_SANITIZER)
157 // Glibc headers hit this on RLIMIT_ macros.
158 #pragma GCC diagnostic push
159 #if defined(__clang__)
160 #pragma GCC diagnostic ignored "-Wdisabled-macro-expansion"
161 #endif
TEST(PagedMemoryTest,Unchecked)162 TEST(PagedMemoryTest, Unchecked) {
163   const size_t kMemLimit = 256 * 1024 * 1024l;
164   struct rlimit limit {
165     kMemLimit, kMemLimit
166   };
167   // ASSERT_EXIT here is to spawn the test in a sub-process and avoid
168   // propagating the setrlimit() to other test units in case of failure.
169   ASSERT_EXIT(
170       {
171         ASSERT_EQ(0, setrlimit(RLIMIT_AS, &limit));
172         auto mem = PagedMemory::Allocate(kMemLimit * 2, PagedMemory::kMayFail);
173         ASSERT_FALSE(mem.IsValid());
174         // Use _exit() instead of exit() to avoid calling destructors on child
175         // process death, which may interfere with the parent process's test
176         // launcher expectations.
177         _exit(0);
178       },
179       ::testing::ExitedWithCode(0), "");
180 }
181 #pragma GCC diagnostic pop
182 #endif
183 
184 }  // namespace
185 }  // namespace base
186 }  // namespace perfetto
187