• 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 #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