1 // Copyright 2015 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 <android/dlext.h>
11 #include <dlfcn.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <inttypes.h>
15 #include <jni.h>
16 #include <link.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22
23 #include <memory>
24
25 // Must come after all headers that specialize FromJniType() / ToJniType().
26 #include "base/android/linker/linker_jni.h"
27
28 namespace chromium_android_linker {
29
30 namespace {
31
32 // Variable containing LibInfo for the loaded library.
33 LibInfo_class s_lib_info_fields;
34
35 // Guarded by |mLock| in Linker.java.
36 RelroSharingStatus s_relro_sharing_status = RelroSharingStatus::NOT_ATTEMPTED;
37
38 // Saved JavaVM passed to JNI_OnLoad().
39 JavaVM* s_java_vm = nullptr;
40
GetPageSize()41 size_t GetPageSize() {
42 return sysconf(_SC_PAGESIZE);
43 }
44
45 // With mmap(2) reserves a range of virtual addresses.
46 //
47 // The range must start with |hint| and be of size |size|. The |hint==0|
48 // indicates that the address of the mapping should be chosen at random,
49 // utilizing ASLR built into mmap(2).
50 //
51 // The start of the resulting region is returned in |address|.
52 //
53 // The value 0 returned iff the attempt failed (a part of the address range is
54 // already reserved by some other subsystem).
ReserveAddressWithHint(uintptr_t hint,uintptr_t * address,size_t * size)55 void ReserveAddressWithHint(uintptr_t hint, uintptr_t* address, size_t* size) {
56 void* ptr = reinterpret_cast<void*>(hint);
57 void* new_ptr = mmap(ptr, kAddressSpaceReservationSize, PROT_NONE,
58 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
59 if (new_ptr == MAP_FAILED) {
60 PLOG_ERROR("mmap");
61 *address = 0;
62 } else if ((hint != 0) && (new_ptr != ptr)) {
63 // Something grabbed the address range before the early phase of the
64 // linker had a chance, this should be uncommon.
65 LOG_ERROR("Address range starting at 0x%" PRIxPTR " was not free to use",
66 hint);
67 munmap(new_ptr, kAddressSpaceReservationSize);
68 *address = 0;
69 } else {
70 *address = reinterpret_cast<uintptr_t>(new_ptr);
71 *size = kAddressSpaceReservationSize;
72 LOG_INFO("Reserved region at address: 0x%" PRIxPTR ", size: 0x%zu",
73 *address, *size);
74 }
75 }
76
ScanRegionInBuffer(const char * buf,size_t length,uintptr_t * out_address,size_t * out_size)77 bool ScanRegionInBuffer(const char* buf,
78 size_t length,
79 uintptr_t* out_address,
80 size_t* out_size) {
81 const char* position = strstr(buf, "[anon:libwebview reservation]");
82 if (!position)
83 return false;
84
85 const char* line_start = position;
86 while (line_start > buf) {
87 line_start--;
88 if (*line_start == '\n') {
89 line_start++;
90 break;
91 }
92 }
93
94 // Extract the region start and end. The failures below should not happen as
95 // long as the reservation is made the same way in
96 // frameworks/base/native/webview/loader/loader.cpp.
97 uintptr_t vma_start, vma_end;
98 char permissions[5] = {'\0'}; // Ensure a null-terminated string.
99 // Example line from proc(5):
100 // address perms offset dev inode pathname
101 // 00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/dbus-daemon
102 if (sscanf(line_start, "%" SCNxPTR "-%" SCNxPTR " %4c", &vma_start, &vma_end,
103 permissions) < 3) {
104 return false;
105 }
106
107 if (strcmp(permissions, "---p"))
108 return false;
109
110 const size_t kPageSize = GetPageSize();
111 if (vma_start % kPageSize || vma_end % kPageSize) {
112 return false;
113 }
114
115 *out_address = static_cast<uintptr_t>(vma_start);
116 *out_size = vma_end - vma_start;
117
118 return true;
119 }
120
FindRegionInOpenFile(int fd,uintptr_t * out_address,size_t * out_size)121 bool FindRegionInOpenFile(int fd, uintptr_t* out_address, size_t* out_size) {
122 constexpr size_t kMaxLineLength = 256;
123 const size_t kPageSize = GetPageSize();
124 const size_t kReadSize = kPageSize;
125
126 // Loop until no bytes left to scan. On every iteration except the last, fill
127 // the buffer till the end. On every iteration except the first, the buffer
128 // begins with kMaxLineLength bytes from the end of the previous fill.
129
130 // Silence clang's warning about allocating on the stack because this is a very
131 // special case.
132 #pragma clang diagnostic push
133 #pragma clang diagnostic ignored "-Wvla-extension"
134 char buf[kReadSize + kMaxLineLength + 1];
135 #pragma clang diagnostic pop
136
137 buf[kReadSize + kMaxLineLength] = '\0'; // Stop strstr().
138 size_t pos = 0;
139 size_t bytes_requested = kReadSize + kMaxLineLength;
140 bool reached_end = false;
141 while (true) {
142 // Fill the |buf| to the maximum and determine whether reading reached the
143 // end.
144 size_t bytes_read = 0;
145 do {
146 ssize_t rv = HANDLE_EINTR(
147 read(fd, buf + pos + bytes_read, bytes_requested - bytes_read));
148 if (rv == 0) {
149 reached_end = true;
150 } else if (rv < 0) {
151 PLOG_ERROR("read to find webview reservation");
152 return false;
153 }
154 bytes_read += rv;
155 } while (!reached_end && (bytes_read < bytes_requested));
156
157 // Return results if the buffer contains the pattern.
158 if (ScanRegionInBuffer(buf, pos + bytes_read, out_address, out_size))
159 return true;
160
161 // Did not find the pattern.
162 if (reached_end)
163 return false;
164
165 // The buffer is filled to the end. Copy the end bytes to the beginning,
166 // allowing to scan these bytes on the next iteration.
167 memcpy(buf, buf + kReadSize, kMaxLineLength);
168 pos = kMaxLineLength;
169 bytes_requested = kReadSize;
170 }
171 }
172
173 // Invokes android_dlopen_ext() to load the library into a given address range.
174 // Assumes that the address range is already reserved with mmap(2). On success,
175 // the |handle| of the loaded library is returned.
176 //
177 // Returns true iff this operation succeeds.
AndroidDlopenExt(void * mapping_start,size_t mapping_size,const char * filename,void ** handle)178 bool AndroidDlopenExt(void* mapping_start,
179 size_t mapping_size,
180 const char* filename,
181 void** handle) {
182 android_dlextinfo dlextinfo = {};
183 dlextinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS;
184 dlextinfo.reserved_addr = mapping_start;
185 dlextinfo.reserved_size = mapping_size;
186
187 LOG_INFO(
188 "android_dlopen_ext:"
189 " flags=0x%" PRIx64 ", reserved_addr=%p, reserved_size=%zu",
190 dlextinfo.flags, dlextinfo.reserved_addr, dlextinfo.reserved_size);
191
192 void* rv = android_dlopen_ext(filename, RTLD_NOW, &dlextinfo);
193 if (rv == nullptr) {
194 LOG_ERROR("android_dlopen_ext: %s", dlerror());
195 return false;
196 }
197
198 *handle = rv;
199 return true;
200 }
201
202 // With munmap(2) unmaps the tail of the given contiguous range of virtual
203 // memory. Ignores errors.
TrimMapping(uintptr_t address,size_t old_size,size_t new_size)204 void TrimMapping(uintptr_t address, size_t old_size, size_t new_size) {
205 if (old_size <= new_size) {
206 LOG_ERROR("WARNING: library reservation was too small");
207 } else {
208 // Unmap the part of the reserved address space that is beyond the end of
209 // the loaded library data.
210 const uintptr_t unmap = address + new_size;
211 const size_t length = old_size - new_size;
212 munmap(reinterpret_cast<void*>(unmap), length);
213 }
214 }
215
216 // Calls JNI_OnLoad() in the library referenced by |handle|.
217 // Returns true for success.
CallJniOnLoad(void * handle)218 bool CallJniOnLoad(void* handle) {
219 LOG_INFO("Entering");
220 // Locate and if found then call the loaded library's JNI_OnLoad() function.
221 using JNI_OnLoadFunctionPtr = int (*)(void* vm, void* reserved);
222 auto jni_onload =
223 reinterpret_cast<JNI_OnLoadFunctionPtr>(dlsym(handle, "JNI_OnLoad"));
224 if (jni_onload != nullptr) {
225 // Check that JNI_OnLoad returns a usable JNI version.
226 int jni_version = (*jni_onload)(s_java_vm, nullptr);
227 if (jni_version < JNI_VERSION_1_4) {
228 LOG_ERROR("JNI version is invalid: %d", jni_version);
229 return false;
230 }
231 }
232
233 LOG_INFO("Done");
234 return true;
235 }
236
237 } // namespace
238
String(JNIEnv * env,jstring str)239 String::String(JNIEnv* env, jstring str) {
240 size_ = env->GetStringUTFLength(str);
241 ptr_ = static_cast<char*>(::malloc(size_ + 1));
242
243 // Note: This runs before browser native code is loaded, and so cannot
244 // rely on anything from base/. This means that we must use
245 // GetStringUTFChars() and not base::android::ConvertJavaStringToUTF8().
246 //
247 // GetStringUTFChars() suffices because the only strings used here are
248 // paths to APK files or names of shared libraries, all of which are
249 // plain ASCII, defined and hard-coded by the Chromium Android build.
250 //
251 // For more: see
252 // https://crbug.com/508876
253 //
254 // Note: GetStringUTFChars() returns Java UTF-8 bytes. This is good
255 // enough for the linker though.
256 const char* bytes = env->GetStringUTFChars(str, nullptr);
257 ::memcpy(ptr_, bytes, size_);
258 ptr_[size_] = '\0';
259
260 env->ReleaseStringUTFChars(str, bytes);
261 }
262
IsValidAddress(jlong address)263 bool IsValidAddress(jlong address) {
264 bool result = static_cast<jlong>(static_cast<uintptr_t>(address)) == address;
265 if (!result) {
266 LOG_ERROR("Invalid address 0x%" PRIx64, static_cast<uint64_t>(address));
267 }
268 return result;
269 }
270
271 // Finds the jclass JNI reference corresponding to a given |class_name|.
272 // |env| is the current JNI environment handle.
273 // On success, return true and set |*clazz|.
InitClassReference(JNIEnv * env,const char * class_name,jclass * clazz)274 bool InitClassReference(JNIEnv* env, const char* class_name, jclass* clazz) {
275 *clazz = env->FindClass(class_name);
276 if (!*clazz) {
277 LOG_ERROR("Could not find class for %s", class_name);
278 return false;
279 }
280 return true;
281 }
282
283 // Initializes a jfieldID corresponding to the field of a given |clazz|,
284 // with name |field_name| and signature |field_sig|.
285 // |env| is the current JNI environment handle.
286 // On success, return true and set |*field_id|.
InitFieldId(JNIEnv * env,jclass clazz,const char * field_name,const char * field_sig,jfieldID * field_id)287 bool InitFieldId(JNIEnv* env,
288 jclass clazz,
289 const char* field_name,
290 const char* field_sig,
291 jfieldID* field_id) {
292 *field_id = env->GetFieldID(clazz, field_name, field_sig);
293 if (!*field_id) {
294 LOG_ERROR("Could not find ID for field '%s'", field_name);
295 return false;
296 }
297 LOG_INFO("Found ID %p for field '%s'", *field_id, field_name);
298 return true;
299 }
300
FindWebViewReservation(uintptr_t * out_address,size_t * out_size)301 bool FindWebViewReservation(uintptr_t* out_address, size_t* out_size) {
302 // Note: reading /proc/PID/maps or /proc/PID/smaps is inherently racy. Among
303 // other things, the kernel provides these guarantees:
304 // * Each region record (line) is well formed
305 // * If there is something at a given vaddr during the entirety of the life of
306 // the smaps/maps walk, there will be some output for it.
307 //
308 // In order for the address/size extraction to be safe, these precausions are
309 // made in base/android/linker:
310 // * Modification of the range is done only after this function exits
311 // * The use of the range is avoided if it is not sufficient in size, which
312 // might happen if it gets split
313 const char kFileName[] = "/proc/self/maps";
314 int fd = HANDLE_EINTR(open(kFileName, O_RDONLY));
315 if (fd == -1) {
316 PLOG_ERROR("open %s", kFileName);
317 return false;
318 }
319
320 bool result = FindRegionInOpenFile(fd, out_address, out_size);
321 close(fd);
322 return result;
323 }
324
325 // Starting with API level 26 (Android O) the following functions from
326 // libandroid.so should be used to create shared memory regions to ensure
327 // compatibility with the future versions:
328 // * ASharedMemory_create()
329 // * ASharedMemory_setProt()
330 //
331 // This is inspired by //third_party/ashmem/ashmem-dev.c, which cannot be
332 // referenced from the linker library to avoid increasing binary size.
333 //
334 // *Not* threadsafe.
335 struct SharedMemoryFunctions {
SharedMemoryFunctionschromium_android_linker::SharedMemoryFunctions336 SharedMemoryFunctions() {
337 library_handle = dlopen("libandroid.so", RTLD_NOW);
338 create = reinterpret_cast<CreateFunction>(
339 dlsym(library_handle, "ASharedMemory_create"));
340 set_protection = reinterpret_cast<SetProtectionFunction>(
341 dlsym(library_handle, "ASharedMemory_setProt"));
342 }
343
IsWorkingchromium_android_linker::SharedMemoryFunctions344 bool IsWorking() const {
345 if (!create || !set_protection) {
346 LOG_ERROR("Cannot get the shared memory functions from libandroid");
347 return false;
348 }
349 return true;
350 }
351
~SharedMemoryFunctionschromium_android_linker::SharedMemoryFunctions352 ~SharedMemoryFunctions() {
353 if (library_handle)
354 dlclose(library_handle);
355 }
356
357 typedef int (*CreateFunction)(const char*, size_t);
358 typedef int (*SetProtectionFunction)(int fd, int prot);
359
360 CreateFunction create;
361 SetProtectionFunction set_protection;
362
363 void* library_handle = nullptr;
364 };
365
ExportLoadInfoToJava() const366 void NativeLibInfo::ExportLoadInfoToJava() const {
367 if (!env_)
368 return;
369 s_lib_info_fields.SetLoadInfo(env_, java_object_, load_address_, load_size_);
370 }
371
ExportRelroInfoToJava() const372 void NativeLibInfo::ExportRelroInfoToJava() const {
373 if (!env_)
374 return;
375 s_lib_info_fields.SetRelroInfo(env_, java_object_, relro_start_, relro_size_,
376 relro_fd_);
377 }
378
CloseRelroFd()379 void NativeLibInfo::CloseRelroFd() {
380 if (relro_fd_ == kInvalidFd)
381 return;
382 close(relro_fd_);
383 relro_fd_ = kInvalidFd;
384 }
385
FindRelroAndLibraryRangesInElf()386 bool NativeLibInfo::FindRelroAndLibraryRangesInElf() {
387 LOG_INFO("Called for 0x%" PRIxPTR, load_address_);
388
389 // Check that an ELF library starts at the |load_address_|.
390 if (memcmp(reinterpret_cast<void*>(load_address_), ELFMAG, SELFMAG) != 0) {
391 LOG_ERROR("Wrong magic number");
392 return false;
393 }
394 auto class_type = *reinterpret_cast<uint8_t*>(load_address_ + EI_CLASS);
395 if (class_type == ELFCLASS32) {
396 LOG_INFO("ELFCLASS32");
397 } else if (class_type == ELFCLASS64) {
398 LOG_INFO("ELFCLASS64");
399 } else {
400 LOG_ERROR("Could not determine ELF class");
401 return false;
402 }
403
404 // Compute the ranges of PT_LOAD segments and the PT_GNU_RELRO. It is possible
405 // to reach for the same information by iterating over all loaded libraries
406 // and their program headers using dl_iterate_phdr(3). Instead here the
407 // iteration goes through the array |e_phoff[e_phnum]| to avoid acquisition of
408 // the global lock in Bionic (dlfcn.cpp).
409 //
410 // The code relies on (1) having RELRO in the PT_GNU_RELRO segment, and (2)
411 // the fact that the address *range* occupied by the library is the minimal
412 // address range containing all of the PT_LOAD and PT_GNU_RELRO segments.
413 // This is a contract between the static linker and the dynamic linker which
414 // seems unlikely to get broken. It might break though as a result of
415 // post-processing the DSO, which has historically happened for a few
416 // occasions (eliminating the unwind tables and splitting the library into
417 // DFMs).
418 auto min_vaddr = std::numeric_limits<ElfW(Addr)>::max();
419 auto min_relro_vaddr = min_vaddr;
420 ElfW(Addr) max_vaddr = 0;
421 ElfW(Addr) max_relro_vaddr = 0;
422 const auto* ehdr = reinterpret_cast<const ElfW(Ehdr)*>(load_address_);
423 const auto* phdrs =
424 reinterpret_cast<const ElfW(Phdr)*>(load_address_ + ehdr->e_phoff);
425 const size_t kPageSize = GetPageSize();
426 for (int i = 0; i < ehdr->e_phnum; i++) {
427 const ElfW(Phdr)* phdr = &phdrs[i];
428 switch (phdr->p_type) {
429 case PT_LOAD:
430 if (phdr->p_vaddr < min_vaddr)
431 min_vaddr = phdr->p_vaddr;
432 if (phdr->p_vaddr + phdr->p_memsz > max_vaddr)
433 max_vaddr = phdr->p_vaddr + phdr->p_memsz;
434 break;
435 case PT_GNU_RELRO:
436 min_relro_vaddr = PageStart(kPageSize, phdr->p_vaddr);
437 max_relro_vaddr = phdr->p_vaddr + phdr->p_memsz;
438
439 // As of 2020-11 in libmonochrome.so RELRO is covered by a LOAD segment.
440 // It is not clear whether this property is going to be guaranteed in
441 // the future. Include the RELRO segment as part of the 'load size'.
442 // This way a potential future change in layout of LOAD segments would
443 // not open address space for racy mmap(MAP_FIXED).
444 if (min_relro_vaddr < min_vaddr)
445 min_vaddr = min_relro_vaddr;
446 if (max_vaddr < max_relro_vaddr)
447 max_vaddr = max_relro_vaddr;
448 break;
449 default:
450 break;
451 }
452 }
453
454 // Fill out size and RELRO information.
455 load_size_ = PageEnd(kPageSize, max_vaddr) - PageStart(kPageSize, min_vaddr);
456 relro_size_ = PageEnd(kPageSize, max_relro_vaddr) -
457 PageStart(kPageSize, min_relro_vaddr);
458 relro_start_ = load_address_ + PageStart(kPageSize, min_relro_vaddr);
459 return true;
460 }
461
LoadWithDlopenExt(const String & path,void ** handle)462 bool NativeLibInfo::LoadWithDlopenExt(const String& path, void** handle) {
463 LOG_INFO("Entering");
464
465 // The address range must be reserved during initialization in Linker.java.
466 if (!load_address_) {
467 // TODO(pasko): measure how often this happens.
468 return false;
469 }
470
471 // Remember the memory reservation size. Starting from this point load_size_
472 // changes the meaning to reflect the size of the loaded library.
473 size_t reservation_size = load_size_;
474 auto* address = reinterpret_cast<void*>(load_address_);
475
476 // Invoke android_dlopen_ext.
477 void* local_handle = nullptr;
478 if (!AndroidDlopenExt(address, reservation_size, path.c_str(),
479 &local_handle)) {
480 LOG_ERROR("android_dlopen_ext() error");
481 munmap(address, load_size_);
482 return false;
483 }
484
485 // Histogram ChromiumAndroidLinker.ModernLinkerDlopenExtTime that measured the
486 // amount of time the ModernLinker spends to run android_dlopen_ext() was
487 // removed in July 2023.
488
489 // Determine the library address ranges and the RELRO region.
490 if (!FindRelroAndLibraryRangesInElf()) {
491 // Fail early if PT_GNU_RELRO is not found. It likely indicates a
492 // build misconfiguration.
493 LOG_ERROR("Could not find RELRO in the loaded library: %s", path.c_str());
494 abort();
495 }
496
497 // Histogram ChromiumAndroidLinker.ModernLinkerIteratePhdrTime that measured
498 // the amount of time the ModernLinker spends to find the RELRO region using
499 // dl_iterate_phdr() was removed in July 2023.
500
501 // Release the unused parts of the memory reservation.
502 TrimMapping(load_address_, reservation_size, load_size_);
503
504 *handle = local_handle;
505 return true;
506 }
507
CreateSharedRelroFd(const SharedMemoryFunctions & functions)508 bool NativeLibInfo::CreateSharedRelroFd(
509 const SharedMemoryFunctions& functions) {
510 LOG_INFO("Entering");
511 if (!relro_start_ || !relro_size_) {
512 LOG_ERROR("RELRO region is not populated");
513 return false;
514 }
515
516 // Create a writable shared memory region.
517 int shared_mem_fd = functions.create("cr_relro", relro_size_);
518 if (shared_mem_fd == -1) {
519 LOG_ERROR("Cannot create the shared memory file");
520 return false;
521 }
522 int rw_flags = PROT_READ | PROT_WRITE;
523 functions.set_protection(shared_mem_fd, rw_flags);
524
525 // Map the region as writable.
526 void* relro_copy_addr =
527 mmap(nullptr, relro_size_, rw_flags, MAP_SHARED, shared_mem_fd, 0);
528 if (relro_copy_addr == MAP_FAILED) {
529 PLOG_ERROR("failed to allocate space for copying RELRO");
530 close(shared_mem_fd);
531 return false;
532 }
533
534 // Populate the shared memory region with the contents of RELRO.
535 void* relro_addr = reinterpret_cast<void*>(relro_start_);
536 memcpy(relro_copy_addr, relro_addr, relro_size_);
537
538 // Protect the underlying physical pages from further modifications from all
539 // processes including the forked ones.
540 //
541 // Setting protection flags on the region to read-only guarantees that the
542 // memory can no longer get mapped as writable, for any FD pointing to the
543 // region, for any process. It is necessary to also munmap(2) the existing
544 // writable memory mappings, since they are not directly affected by the
545 // change of region's protection flags.
546 munmap(relro_copy_addr, relro_size_);
547 if (functions.set_protection(shared_mem_fd, PROT_READ) == -1) {
548 LOG_ERROR("Failed to set the RELRO FD as read-only.");
549 close(shared_mem_fd);
550 return false;
551 }
552
553 relro_fd_ = shared_mem_fd;
554 return true;
555 }
556
ReplaceRelroWithSharedOne(const SharedMemoryFunctions & functions) const557 bool NativeLibInfo::ReplaceRelroWithSharedOne(
558 const SharedMemoryFunctions& functions) const {
559 LOG_INFO("Entering");
560 if (relro_fd_ == -1 || !relro_start_ || !relro_size_) {
561 LOG_ERROR("Replacement RELRO not ready");
562 return false;
563 }
564
565 // Map as read-only to *atomically* replace the RELRO region provided by the
566 // dynamic linker. To avoid memory corruption it is important that the
567 // contents of both memory regions is identical.
568 void* new_addr = mmap(reinterpret_cast<void*>(relro_start_), relro_size_,
569 PROT_READ, MAP_FIXED | MAP_SHARED, relro_fd_, 0);
570 if (new_addr == MAP_FAILED) {
571 PLOG_ERROR("mmap: replace RELRO");
572 return false;
573 }
574
575 LOG_INFO("Replaced RELRO at 0x%" PRIxPTR, relro_start_);
576 return true;
577 }
578
NativeLibInfo(JNIEnv * env,jobject java_object)579 NativeLibInfo::NativeLibInfo(JNIEnv* env, jobject java_object)
580 : env_(env), java_object_(java_object) {}
581
CopyFromJavaObject()582 bool NativeLibInfo::CopyFromJavaObject() {
583 if (!env_)
584 return false;
585
586 if (!s_lib_info_fields.GetLoadInfo(env_, java_object_, &load_address_,
587 &load_size_)) {
588 return false;
589 }
590 s_lib_info_fields.GetRelroInfo(env_, java_object_, &relro_start_,
591 &relro_size_, &relro_fd_);
592 return true;
593 }
594
LoadLibrary(const String & library_path,bool spawn_relro_region)595 bool NativeLibInfo::LoadLibrary(const String& library_path,
596 bool spawn_relro_region) {
597 // Load the library.
598 void* handle = nullptr;
599 if (!LoadWithDlopenExt(library_path, &handle)) {
600 LOG_ERROR("Failed to load native library: %s", library_path.c_str());
601 return false;
602 }
603 if (!CallJniOnLoad(handle))
604 return false;
605
606 // Publish the library size and load address back to LibInfo in Java.
607 ExportLoadInfoToJava();
608
609 if (!spawn_relro_region)
610 return true;
611
612 // Spawn RELRO to a shared memory region by copying and remapping on top of
613 // itself.
614 SharedMemoryFunctions functions;
615 if (!functions.IsWorking())
616 return false;
617 if (!CreateSharedRelroFd(functions)) {
618 LOG_ERROR("Failed to create shared RELRO");
619 return false;
620 }
621 if (!ReplaceRelroWithSharedOne(functions)) {
622 LOG_ERROR("Failed to convert RELRO to shared memory");
623 CloseRelroFd();
624 return false;
625 }
626
627 LOG_INFO(
628 "Created and converted RELRO to shared memory: relro_fd=%d, "
629 "relro_start=0x%" PRIxPTR,
630 relro_fd_, relro_start_);
631 ExportRelroInfoToJava();
632 return true;
633 }
634
RelroIsIdentical(const NativeLibInfo & other_lib_info,const SharedMemoryFunctions & functions) const635 bool NativeLibInfo::RelroIsIdentical(
636 const NativeLibInfo& other_lib_info,
637 const SharedMemoryFunctions& functions) const {
638 // Abandon sharing if contents of the incoming RELRO region does not match the
639 // current one. This can be useful for debugging, but should never happen in
640 // the field.
641 if (other_lib_info.relro_start_ != relro_start_ ||
642 other_lib_info.relro_size_ != relro_size_ ||
643 other_lib_info.load_size_ != load_size_) {
644 LOG_ERROR("Incoming RELRO size does not match RELRO of the loaded library");
645 return false;
646 }
647 void* shared_relro_address =
648 mmap(nullptr, other_lib_info.relro_size_, PROT_READ, MAP_SHARED,
649 other_lib_info.relro_fd_, 0);
650 if (shared_relro_address == MAP_FAILED) {
651 PLOG_ERROR("mmap: check RELRO is identical");
652 return false;
653 }
654 void* current_relro_address = reinterpret_cast<void*>(relro_start_);
655 int not_equal =
656 memcmp(shared_relro_address, current_relro_address, relro_size_);
657 munmap(shared_relro_address, relro_size_);
658 if (not_equal) {
659 LOG_ERROR("Relocations are not identical, giving up.");
660 return false;
661 }
662 return true;
663 }
664
CompareRelroAndReplaceItBy(const NativeLibInfo & other_lib_info)665 bool NativeLibInfo::CompareRelroAndReplaceItBy(
666 const NativeLibInfo& other_lib_info) {
667 if (other_lib_info.relro_fd_ == -1) {
668 LOG_ERROR("No shared region to use");
669 s_relro_sharing_status = RelroSharingStatus::EXTERNAL_RELRO_FD_NOT_PROVIDED;
670 return false;
671 }
672
673 if (load_address_ == 0) {
674 LOG_ERROR("Load address reset. Second attempt to load the library?");
675 s_relro_sharing_status = RelroSharingStatus::EXTERNAL_LOAD_ADDRESS_RESET;
676 return false;
677 }
678
679 if (!FindRelroAndLibraryRangesInElf()) {
680 LOG_ERROR("Could not find RELRO from externally provided address: 0x%p",
681 reinterpret_cast<void*>(other_lib_info.load_address_));
682 s_relro_sharing_status = RelroSharingStatus::EXTERNAL_RELRO_NOT_FOUND;
683 return false;
684 }
685
686 SharedMemoryFunctions functions;
687 if (!functions.IsWorking()) {
688 s_relro_sharing_status = RelroSharingStatus::NO_SHMEM_FUNCTIONS;
689 return false;
690 }
691 if (!RelroIsIdentical(other_lib_info, functions)) {
692 LOG_ERROR("RELRO is not identical");
693 s_relro_sharing_status = RelroSharingStatus::NOT_IDENTICAL;
694 return false;
695 }
696
697 // Make it shared.
698 //
699 // The alternative approach to invoke mprotect+mremap is probably faster than
700 // munmap+mmap here. The advantage of the latter is that it removes all
701 // formerly writable mappings, so:
702 // * It does not rely on disallowing mprotect(PROT_WRITE)
703 // * This way |ReplaceRelroWithSharedOne()| is reused across spawning RELRO
704 // and receiving it
705 if (!other_lib_info.ReplaceRelroWithSharedOne(functions)) {
706 LOG_ERROR("Failed to use relro_fd");
707 s_relro_sharing_status = RelroSharingStatus::REMAP_FAILED;
708 return false;
709 }
710
711 s_relro_sharing_status = RelroSharingStatus::SHARED;
712 return true;
713 }
714
CreateSharedRelroFdForTesting()715 bool NativeLibInfo::CreateSharedRelroFdForTesting() {
716 // The library providing these functions will be dlclose()-ed after returning
717 // from this context. The extra overhead of dlopen() is OK for testing.
718 SharedMemoryFunctions functions;
719 if (!functions.IsWorking())
720 abort();
721 return CreateSharedRelroFd(functions);
722 }
723
724 // static
SharedMemoryFunctionsSupportedForTesting()725 bool NativeLibInfo::SharedMemoryFunctionsSupportedForTesting() {
726 SharedMemoryFunctions functions;
727 return functions.IsWorking();
728 }
729
730 JNI_ZERO_BOUNDARY_EXPORT void
Java_org_chromium_base_library_1loader_LinkerJni_nativeFindMemoryRegionAtRandomAddress(JNIEnv * env,jclass clazz,jobject lib_info_obj)731 Java_org_chromium_base_library_1loader_LinkerJni_nativeFindMemoryRegionAtRandomAddress(
732 JNIEnv* env,
733 jclass clazz,
734 jobject lib_info_obj) {
735 LOG_INFO("Entering");
736 uintptr_t address;
737 size_t size;
738 ReserveAddressWithHint(0, &address, &size);
739 s_lib_info_fields.SetLoadInfo(env, lib_info_obj, address, size);
740 }
741
742 JNI_ZERO_BOUNDARY_EXPORT void
Java_org_chromium_base_library_1loader_LinkerJni_nativeReserveMemoryForLibrary(JNIEnv * env,jclass clazz,jobject lib_info_obj)743 Java_org_chromium_base_library_1loader_LinkerJni_nativeReserveMemoryForLibrary(
744 JNIEnv* env,
745 jclass clazz,
746 jobject lib_info_obj) {
747 LOG_INFO("Entering");
748 uintptr_t address;
749 size_t size;
750 s_lib_info_fields.GetLoadInfo(env, lib_info_obj, &address, &size);
751 ReserveAddressWithHint(address, &address, &size);
752 s_lib_info_fields.SetLoadInfo(env, lib_info_obj, address, size);
753 }
754
755 JNI_ZERO_BOUNDARY_EXPORT jboolean
Java_org_chromium_base_library_1loader_LinkerJni_nativeFindRegionReservedByWebViewZygote(JNIEnv * env,jclass clazz,jobject lib_info_obj)756 Java_org_chromium_base_library_1loader_LinkerJni_nativeFindRegionReservedByWebViewZygote(
757 JNIEnv* env,
758 jclass clazz,
759 jobject lib_info_obj) {
760 LOG_INFO("Entering");
761 uintptr_t address;
762 size_t size;
763 if (!FindWebViewReservation(&address, &size))
764 return false;
765 s_lib_info_fields.SetLoadInfo(env, lib_info_obj, address, size);
766 return true;
767 }
768
769 JNI_ZERO_BOUNDARY_EXPORT jboolean
Java_org_chromium_base_library_1loader_LinkerJni_nativeLoadLibrary(JNIEnv * env,jclass clazz,jstring jdlopen_ext_path,jobject lib_info_obj,jboolean spawn_relro_region)770 Java_org_chromium_base_library_1loader_LinkerJni_nativeLoadLibrary(
771 JNIEnv* env,
772 jclass clazz,
773 jstring jdlopen_ext_path,
774 jobject lib_info_obj,
775 jboolean spawn_relro_region) {
776 LOG_INFO("Entering");
777
778 // Copy the contents from the Java-side LibInfo object.
779 NativeLibInfo lib_info = {env, lib_info_obj};
780 if (!lib_info.CopyFromJavaObject())
781 return false;
782
783 String library_path(env, jdlopen_ext_path);
784 if (!lib_info.LoadLibrary(library_path, spawn_relro_region)) {
785 return false;
786 }
787 return true;
788 }
789
790 JNI_ZERO_BOUNDARY_EXPORT jboolean
Java_org_chromium_base_library_1loader_LinkerJni_nativeUseRelros(JNIEnv * env,jclass clazz,jlong local_load_address,jobject remote_lib_info_obj)791 Java_org_chromium_base_library_1loader_LinkerJni_nativeUseRelros(
792 JNIEnv* env,
793 jclass clazz,
794 jlong local_load_address,
795 jobject remote_lib_info_obj) {
796 LOG_INFO("Entering");
797 // Copy the contents from the Java-side LibInfo object.
798 NativeLibInfo incoming_lib_info = {env, remote_lib_info_obj};
799 if (!incoming_lib_info.CopyFromJavaObject()) {
800 s_relro_sharing_status = RelroSharingStatus::CORRUPTED_IN_JAVA;
801 return false;
802 }
803
804 // Create an empty NativeLibInfo to extract the current information about the
805 // loaded library and later compare with the contents of the
806 // |incoming_lib_info|.
807 NativeLibInfo lib_info = {nullptr, nullptr};
808 lib_info.set_load_address(static_cast<uintptr_t>(local_load_address));
809
810 if (!lib_info.CompareRelroAndReplaceItBy(incoming_lib_info)) {
811 return false;
812 }
813 return true;
814 }
815
816 JNI_ZERO_BOUNDARY_EXPORT jint
Java_org_chromium_base_library_1loader_LinkerJni_nativeGetRelroSharingResult(JNIEnv * env,jclass clazz)817 Java_org_chromium_base_library_1loader_LinkerJni_nativeGetRelroSharingResult(
818 JNIEnv* env,
819 jclass clazz) {
820 return static_cast<jint>(s_relro_sharing_status);
821 }
822
LinkerJNIInit(JavaVM * vm,JNIEnv * env)823 bool LinkerJNIInit(JavaVM* vm, JNIEnv* env) {
824 // Find LibInfo field ids.
825 if (!s_lib_info_fields.Init(env)) {
826 return false;
827 }
828
829 s_java_vm = vm;
830 return true;
831 }
832
833 } // namespace chromium_android_linker
834