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