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