• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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