1 // Copyright 2020 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 <link.h>
6 #include <sys/mman.h>
7 #include <sys/prctl.h>
8 #include <sys/utsname.h>
9
10 #include "base/android/linker/linker_jni.h"
11 #include "base/files/scoped_file.h"
12 #include "base/logging.h"
13 #include "base/system/sys_info.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 extern char __executable_start;
17
18 extern "C" {
19
20 // This function is exported by the dynamic linker but never declared in any
21 // official header for some architecture/version combinations.
22 int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data),
23 void* data) __attribute__((weak_import));
24
25 } // extern "C"
26
27 namespace chromium_android_linker {
28
29 namespace {
30
31 // Implements the old method of finding library and RELRO ranges by providing a
32 // callback for use with dl_iterate_phdr(3). Data from the field has shown that
33 // this method makes library loading significantly slower than
34 // android_dlopen_ext(), it was replaced by the exuivalent one:
35 // NativeLibInfo::FindRelroAndLibraryRangesInElf().
36 class LibraryRangeFinder {
37 public:
LibraryRangeFinder(uintptr_t address)38 explicit LibraryRangeFinder(uintptr_t address) : load_address_(address) {}
39
load_address() const40 uintptr_t load_address() const { return load_address_; }
load_size() const41 size_t load_size() const { return load_size_; }
relro_start() const42 uintptr_t relro_start() const { return relro_start_; }
relro_size() const43 size_t relro_size() const { return relro_size_; }
44
45 static int VisitLibraryPhdrs(dl_phdr_info* info,
46 [[maybe_unused]] size_t size,
47 void* data);
48
49 private:
50 uintptr_t load_address_;
51 size_t load_size_ = 0;
52 uintptr_t relro_start_ = 0;
53 size_t relro_size_ = 0;
54 };
55
56 // Callback for dl_iterate_phdr(). From program headers (phdr(s)) of a loaded
57 // library determines its load address, and in case it is equal to
58 // |load_address()|, extracts the RELRO and size information from
59 // corresponding phdr(s).
60 // static
VisitLibraryPhdrs(dl_phdr_info * info,size_t size,void * data)61 int LibraryRangeFinder::VisitLibraryPhdrs(dl_phdr_info* info,
62 [[maybe_unused]] size_t size,
63 void* data) {
64 auto* finder = reinterpret_cast<LibraryRangeFinder*>(data);
65 ElfW(Addr) lookup_address = static_cast<ElfW(Addr)>(finder->load_address());
66
67 // Use max and min vaddr to compute the library's load size.
68 auto min_vaddr = std::numeric_limits<ElfW(Addr)>::max();
69 ElfW(Addr) max_vaddr = 0;
70 ElfW(Addr) min_relro_vaddr = ~0;
71 ElfW(Addr) max_relro_vaddr = 0;
72
73 const size_t kPageSize = sysconf(_SC_PAGESIZE);
74 bool is_matching = false;
75 for (int i = 0; i < info->dlpi_phnum; ++i) {
76 const ElfW(Phdr)* phdr = &info->dlpi_phdr[i];
77 switch (phdr->p_type) {
78 case PT_LOAD:
79 // See if this segment's load address matches the value passed to
80 // android_dlopen_ext as |extinfo.reserved_addr|.
81 //
82 // Here and below, the virtual address in memory is computed by
83 // address == info->dlpi_addr + program_header->p_vaddr
84 // that is, the p_vaddr fields is relative to the object base address.
85 // See dl_iterate_phdr(3) for details.
86 if (lookup_address == info->dlpi_addr + phdr->p_vaddr)
87 is_matching = true;
88
89 if (phdr->p_vaddr < min_vaddr)
90 min_vaddr = phdr->p_vaddr;
91 if (phdr->p_vaddr + phdr->p_memsz > max_vaddr)
92 max_vaddr = phdr->p_vaddr + phdr->p_memsz;
93 break;
94 case PT_GNU_RELRO:
95 min_relro_vaddr = PageStart(kPageSize, phdr->p_vaddr);
96 max_relro_vaddr = phdr->p_vaddr + phdr->p_memsz;
97
98 // As of 2020-11 in libmonochrome.so RELRO is covered by a LOAD segment.
99 // It is not clear whether this property is going to be guaranteed in
100 // the future. Include the RELRO segment as part of the 'load size'.
101 // This way a potential future change in layout of LOAD segments would
102 // not open address space for racy mmap(MAP_FIXED).
103 if (min_relro_vaddr < min_vaddr)
104 min_vaddr = min_relro_vaddr;
105 if (max_vaddr < max_relro_vaddr)
106 max_vaddr = max_relro_vaddr;
107 break;
108 default:
109 break;
110 }
111 }
112
113 // Fill out size and relro information if there was a match.
114 if (is_matching) {
115 finder->load_size_ =
116 PageEnd(kPageSize, max_vaddr) - PageStart(kPageSize, min_vaddr);
117 finder->relro_size_ = PageEnd(kPageSize, max_relro_vaddr) -
118 PageStart(kPageSize, min_relro_vaddr);
119 finder->relro_start_ =
120 info->dlpi_addr + PageStart(kPageSize, min_relro_vaddr);
121
122 return 1;
123 }
124
125 return 0;
126 }
127
128 } // namespace
129
130 // These tests get linked with base_unittests and leave JNI uninitialized. The
131 // tests must not execute any parts relying on initialization with JNI_Onload().
132
133 class LinkerTest : public testing::Test {
134 public:
135 LinkerTest() = default;
136 ~LinkerTest() override = default;
137 };
138
139 // Checks that NativeLibInfo::CreateSharedRelroFd() creates a shared memory
140 // region that is 'sealed' as read-only.
TEST_F(LinkerTest,CreatedRegionIsSealed)141 TEST_F(LinkerTest, CreatedRegionIsSealed) {
142 if (!NativeLibInfo::SharedMemoryFunctionsSupportedForTesting()) {
143 // The Linker uses functions from libandroid.so that are not available
144 // on Android releases before O. Disable unittests for old releases.
145 return;
146 }
147
148 // Fill a synthetic RELRO region with 0xEE in private anonynous memory.
149 constexpr size_t kRelroSize = 1 << 21; // 2 MiB.
150 void* relro_address = mmap(nullptr, kRelroSize, PROT_READ | PROT_WRITE,
151 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
152 ASSERT_NE(MAP_FAILED, relro_address);
153 NativeLibInfo lib_info = {0, 0};
154 lib_info.set_relro_info_for_testing(
155 reinterpret_cast<uintptr_t>(relro_address), kRelroSize);
156 memset(relro_address, 0xEE, kRelroSize);
157
158 // Create shared RELRO.
159 ASSERT_EQ(true, lib_info.CreateSharedRelroFdForTesting());
160 int relro_fd = lib_info.get_relro_fd_for_testing();
161 ASSERT_NE(-1, relro_fd);
162 base::ScopedFD scoped_fd(relro_fd);
163
164 // Check that a read-only mapping contains the data originally filled in.
165 void* ro_address =
166 mmap(nullptr, kRelroSize, PROT_READ, MAP_SHARED, relro_fd, 0);
167 ASSERT_NE(MAP_FAILED, ro_address);
168 EXPECT_EQ(0xEEEEEEEEU, *reinterpret_cast<uint32_t*>(ro_address));
169 int not_equal = memcmp(relro_address, ro_address, kRelroSize);
170 EXPECT_EQ(0, not_equal);
171 munmap(ro_address, kRelroSize);
172
173 // Check that attempts to mmap with PROT_WRITE fail.
174 EXPECT_EQ(MAP_FAILED, mmap(nullptr, kRelroSize, PROT_READ | PROT_WRITE,
175 MAP_SHARED, relro_fd, 0));
176 EXPECT_EQ(MAP_FAILED, mmap(nullptr, kRelroSize, PROT_READ | PROT_WRITE,
177 MAP_PRIVATE, relro_fd, 0));
178 EXPECT_EQ(MAP_FAILED,
179 mmap(nullptr, kRelroSize, PROT_WRITE, MAP_SHARED, relro_fd, 0));
180 EXPECT_EQ(MAP_FAILED,
181 mmap(nullptr, kRelroSize, PROT_WRITE, MAP_PRIVATE, relro_fd, 0));
182 }
183
TEST_F(LinkerTest,FindReservedMemoryRegion)184 TEST_F(LinkerTest, FindReservedMemoryRegion) {
185 size_t address, size;
186
187 // Find the existing reservation in the current process. The unittest runner
188 // is forked from the system zygote. The reservation should be found when
189 // running on recent Android releases, where it is made by the
190 // reserveAddressSpaceInZygote().
191 bool found_reservation = FindWebViewReservation(&address, &size);
192
193 if (found_reservation) {
194 // Check that the size is at least the minimum reserved by Android, as of
195 // 2021-04.
196 EXPECT_LE(130U * 1024 * 1024, size);
197 return;
198 }
199
200 // TODO(crbug.com/1223747): Check that only non-low-end Android Q+ devices
201 // reach this point.
202
203 // Create a properly named synthetic region with a size smaller than a real
204 // library would need, but still aligned well.
205 static const size_t kSize = 19U * 1024 * 1024;
206 void* synthetic_region_start =
207 mmap(nullptr, kSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
208 ASSERT_NE(MAP_FAILED, synthetic_region_start);
209 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, synthetic_region_start, kSize,
210 "[anon:libwebview reservation]");
211
212 // Now the region must be found.
213 EXPECT_TRUE(FindWebViewReservation(&address, &size));
214 EXPECT_EQ(kSize, size);
215 EXPECT_EQ(reinterpret_cast<void*>(address), synthetic_region_start);
216 munmap(synthetic_region_start, kSize);
217 }
218
TEST_F(LinkerTest,FindLibraryRanges)219 TEST_F(LinkerTest, FindLibraryRanges) {
220 static int var_inside = 3;
221
222 NativeLibInfo lib_info = {0, 0};
223 uintptr_t executable_start = reinterpret_cast<uintptr_t>(&__executable_start);
224 lib_info.set_load_address(executable_start);
225
226 EXPECT_TRUE(lib_info.FindRelroAndLibraryRangesInElfForTesting());
227 EXPECT_EQ(executable_start, lib_info.load_address());
228
229 uintptr_t inside_library = reinterpret_cast<uintptr_t>(&var_inside);
230 EXPECT_LE(executable_start, inside_library);
231 EXPECT_LE(inside_library,
232 lib_info.load_address() + lib_info.get_load_size_for_testing());
233
234 EXPECT_LE(lib_info.load_address(), lib_info.get_relro_start_for_testing());
235 EXPECT_LE(lib_info.get_relro_start_for_testing(),
236 lib_info.load_address() + lib_info.get_load_size_for_testing());
237 }
238
TEST_F(LinkerTest,FindLibraryRangesWhenLoadAddressWasReset)239 TEST_F(LinkerTest, FindLibraryRangesWhenLoadAddressWasReset) {
240 NativeLibInfo other_lib_info = {0, 0};
241 uintptr_t executable_start = reinterpret_cast<uintptr_t>(&__executable_start);
242 other_lib_info.set_load_address(executable_start);
243 other_lib_info.set_relro_fd_for_testing(123);
244 NativeLibInfo lib_info = {0, 0};
245 EXPECT_FALSE(lib_info.CompareRelroAndReplaceItBy(other_lib_info));
246 }
247
248 // Check that discovering RELRO segment address ranges and the DSO ranges agrees
249 // with the method based on dl_iterate_phdr(3). The check is performed on the
250 // test library, not on libmonochrome.
TEST_F(LinkerTest,LibraryRangesViaIteratePhdr)251 TEST_F(LinkerTest, LibraryRangesViaIteratePhdr) {
252 // Find the ranges using dl_iterate_phdr().
253 if (!dl_iterate_phdr) {
254 ASSERT_TRUE(false) << "dl_iterate_phdr() not found";
255 }
256 uintptr_t executable_start = reinterpret_cast<uintptr_t>(&__executable_start);
257 LibraryRangeFinder finder(executable_start);
258 ASSERT_EQ(1, dl_iterate_phdr(&LibraryRangeFinder::VisitLibraryPhdrs,
259 reinterpret_cast<void*>(&finder)));
260 ASSERT_LE(finder.relro_start() + finder.relro_size(),
261 finder.load_address() + finder.load_size());
262
263 // Find the ranges by parsing ELF.
264 NativeLibInfo lib_info2 = {0, 0};
265 lib_info2.set_load_address(executable_start);
266 EXPECT_TRUE(lib_info2.FindRelroAndLibraryRangesInElfForTesting());
267
268 // Compare results.
269 EXPECT_EQ(finder.load_address(), lib_info2.load_address());
270 EXPECT_EQ(finder.load_size(), lib_info2.get_load_size_for_testing());
271 EXPECT_EQ(finder.relro_start(), lib_info2.get_relro_start_for_testing());
272 }
273
274 } // namespace chromium_android_linker
275