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