• 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 // This is the Android-specific Chromium dynamic linker (loader of dynamic
6 // libraries), a tiny shared library implementing a custom dynamic linker that
7 // can be used to load the real Chromium libraries.
8 //
9 // The purpose of this custom linker is to be able to share the RELRO section of
10 // libcontentshell.so (or equivalent) between the browser process and all other
11 // processes it asks to create.
12 //
13 // This source code *cannot* depend on anything from //base or the C++ standard
14 // library to keep this DSO small and avoid dependency issues. An exception is
15 // made for std::unique_ptr as a risky header-only definition.
16 
17 #ifndef BASE_ANDROID_LINKER_LINKER_JNI_H_
18 #define BASE_ANDROID_LINKER_LINKER_JNI_H_
19 
20 #include <android/log.h>
21 #include <jni.h>
22 #include <stdlib.h>
23 
24 #include "build/build_config.h"
25 
26 // Set this to 1 to enable debug traces to the Android log.
27 // Note that LOG() from "base/logging.h" cannot be used, since it is
28 // in base/ which hasn't been loaded yet.
29 #define DEBUG 0
30 
31 #define TAG "cr_ChromiumAndroidLinker"
32 
33 #if DEBUG
34 #define LOG_INFO(FORMAT, ...)                                             \
35   __android_log_print(ANDROID_LOG_INFO, TAG, "%s: " FORMAT, __FUNCTION__, \
36                       ##__VA_ARGS__)
37 #else
38 #define LOG_INFO(FORMAT, ...) ((void)0)
39 #endif
40 #define LOG_ERROR(FORMAT, ...)                                             \
41   __android_log_print(ANDROID_LOG_ERROR, TAG, "%s: " FORMAT, __FUNCTION__, \
42                       ##__VA_ARGS__)
43 #define PLOG_ERROR(FORMAT, ...) \
44   LOG_ERROR(FORMAT ": %s", ##__VA_ARGS__, strerror(errno))
45 
46 #if defined(ARCH_CPU_X86)
47 // Dalvik JIT generated code doesn't guarantee 16-byte stack alignment on
48 // x86 - use force_align_arg_pointer to realign the stack at the JNI
49 // boundary. https://crbug.com/655248
50 #define JNI_GENERATOR_EXPORT \
51   extern "C" __attribute__((visibility("default"), force_align_arg_pointer))
52 #else
53 #define JNI_GENERATOR_EXPORT extern "C" __attribute__((visibility("default")))
54 #endif
55 
56 #if defined(__arm__) && defined(__ARM_ARCH_7A__)
57 #define CURRENT_ABI "armeabi-v7a"
58 #elif defined(__arm__)
59 #define CURRENT_ABI "armeabi"
60 #elif defined(__i386__)
61 #define CURRENT_ABI "x86"
62 #elif defined(__mips__)
63 #define CURRENT_ABI "mips"
64 #elif defined(__x86_64__)
65 #define CURRENT_ABI "x86_64"
66 #elif defined(__aarch64__)
67 #define CURRENT_ABI "arm64-v8a"
68 #else
69 #error "Unsupported target abi"
70 #endif
71 
72 #if !defined(PAGE_SIZE)
73 #define PAGE_SIZE (1 << 12)
74 #define PAGE_MASK (~(PAGE_SIZE - 1))
75 #endif
76 
77 #define PAGE_START(x) ((x)&PAGE_MASK)
78 #define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
79 
80 // Copied from //base/posix/eintr_wrapper.h to avoid depending on //base.
81 #define HANDLE_EINTR(x)                                     \
82   ({                                                        \
83     decltype(x) eintr_wrapper_result;                       \
84     do {                                                    \
85       eintr_wrapper_result = (x);                           \
86     } while (eintr_wrapper_result == -1 && errno == EINTR); \
87     eintr_wrapper_result;                                   \
88   })
89 
90 namespace chromium_android_linker {
91 
92 // Larger than the largest library we might attempt to load.
93 static const size_t kAddressSpaceReservationSize = 192 * 1024 * 1024;
94 
95 // A simple scoped UTF String class that can be initialized from
96 // a Java jstring handle. Modeled like std::string, which cannot
97 // be used here.
98 class String {
99  public:
100   String(JNIEnv* env, jstring str);
101 
~String()102   inline ~String() { ::free(ptr_); }
103 
c_str()104   inline const char* c_str() const { return ptr_ ? ptr_ : ""; }
size()105   inline size_t size() const { return size_; }
106 
107  private:
108   char* ptr_;
109   size_t size_;
110 };
111 
112 // Returns true iff casting a java-side |address| to uintptr_t does not lose
113 // bits.
114 bool IsValidAddress(jlong address);
115 
116 // Find the jclass JNI reference corresponding to a given |class_name|.
117 // |env| is the current JNI environment handle.
118 // On success, return true and set |*clazz|.
119 bool InitClassReference(JNIEnv* env, const char* class_name, jclass* clazz);
120 
121 // Finds the region reserved by the WebView zygote if the current process is
122 // inherited from the modern enough zygote that has this reservation. If the
123 // lookup is successful, returns true and sets |address| and |size|. Otherwise
124 // returns false.
125 bool FindWebViewReservation(uintptr_t* address, size_t* size);
126 
127 // Initialize a jfieldID corresponding to the field of a given |clazz|,
128 // with name |field_name| and signature |field_sig|.
129 // |env| is the current JNI environment handle.
130 // On success, return true and set |*field_id|.
131 bool InitFieldId(JNIEnv* env,
132                  jclass clazz,
133                  const char* field_name,
134                  const char* field_sig,
135                  jfieldID* field_id);
136 
137 // Initialize a jfieldID corresponding to the static field of a given |clazz|,
138 // with name |field_name| and signature |field_sig|.
139 // |env| is the current JNI environment handle.
140 // On success, return true and set |*field_id|.
141 bool InitStaticFieldId(JNIEnv* env,
142                        jclass clazz,
143                        const char* field_name,
144                        const char* field_sig,
145                        jfieldID* field_id);
146 
147 // A class used to model the field IDs of the org.chromium.base.Linker
148 // LibInfo inner class, used to communicate data with the Java side
149 // of the linker.
150 struct LibInfo_class {
151   jfieldID load_address_id;
152   jfieldID load_size_id;
153   jfieldID relro_start_id;
154   jfieldID relro_size_id;
155   jfieldID relro_fd_id;
156 
157   // Initialize an instance.
InitLibInfo_class158   bool Init(JNIEnv* env) {
159     jclass clazz;
160     if (!InitClassReference(
161             env, "org/chromium/base/library_loader/Linker$LibInfo", &clazz)) {
162       return false;
163     }
164 
165     return InitFieldId(env, clazz, "mLoadAddress", "J", &load_address_id) &&
166            InitFieldId(env, clazz, "mLoadSize", "J", &load_size_id) &&
167            InitFieldId(env, clazz, "mRelroStart", "J", &relro_start_id) &&
168            InitFieldId(env, clazz, "mRelroSize", "J", &relro_size_id) &&
169            InitFieldId(env, clazz, "mRelroFd", "I", &relro_fd_id);
170   }
171 
SetLoadInfoLibInfo_class172   void SetLoadInfo(JNIEnv* env,
173                    jobject library_info_obj,
174                    uintptr_t load_address,
175                    size_t load_size) {
176     env->SetLongField(library_info_obj, load_address_id, load_address);
177     env->SetLongField(library_info_obj, load_size_id, load_size);
178   }
179 
SetRelroInfoLibInfo_class180   void SetRelroInfo(JNIEnv* env,
181                     jobject library_info_obj,
182                     uintptr_t relro_start,
183                     size_t relro_size,
184                     int relro_fd) {
185     env->SetLongField(library_info_obj, relro_start_id, relro_start);
186     env->SetLongField(library_info_obj, relro_size_id, relro_size);
187     env->SetIntField(library_info_obj, relro_fd_id, relro_fd);
188   }
189 
GetLoadInfoLibInfo_class190   bool GetLoadInfo(JNIEnv* env,
191                    jobject library_info_obj,
192                    uintptr_t* load_address,
193                    size_t* load_size) {
194     if (load_address) {
195       jlong java_address = env->GetLongField(library_info_obj, load_address_id);
196       if (!IsValidAddress(java_address))
197         return false;
198       *load_address = static_cast<uintptr_t>(java_address);
199     }
200     if (load_size) {
201       *load_size = static_cast<uintptr_t>(
202           env->GetLongField(library_info_obj, load_size_id));
203     }
204     return true;
205   }
206 
GetRelroInfoLibInfo_class207   void GetRelroInfo(JNIEnv* env,
208                     jobject library_info_obj,
209                     uintptr_t* relro_start,
210                     size_t* relro_size,
211                     int* relro_fd) {
212     if (relro_start) {
213       *relro_start = static_cast<uintptr_t>(
214           env->GetLongField(library_info_obj, relro_start_id));
215     }
216 
217     if (relro_size) {
218       *relro_size = static_cast<size_t>(
219           env->GetLongField(library_info_obj, relro_size_id));
220     }
221 
222     if (relro_fd) {
223       *relro_fd = env->GetIntField(library_info_obj, relro_fd_id);
224     }
225   }
226 };
227 
228 // Used to find out whether RELRO sharing is often rejected due to mismatch of
229 // the contents.
230 //
231 // These values are persisted to logs. Entries should not be renumbered and
232 // numeric values should never be reused. Must be kept in sync with the enum
233 // in enums.xml. A java @IntDef is generated from this.
234 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.library_loader
235 enum class RelroSharingStatus {
236   NOT_ATTEMPTED = 0,
237   SHARED = 1,
238   NOT_IDENTICAL = 2,
239   EXTERNAL_RELRO_FD_NOT_PROVIDED = 3,
240   EXTERNAL_RELRO_NOT_FOUND = 4,
241   NO_SHMEM_FUNCTIONS = 5,
242   REMAP_FAILED = 6,
243   CORRUPTED_IN_JAVA = 7,
244   EXTERNAL_LOAD_ADDRESS_RESET = 8,
245   COUNT = 9,
246 };
247 
248 struct SharedMemoryFunctions;
249 
250 // Abstract class for NativeLibInfo to use for miscellaneous time measurements.
251 // Best to be provided with values from the same clock as
252 // SystemClock.uptimeMillis().
253 //
254 // *Not* threadsafe.
255 class LoadTimeReporter {
256  public:
257   virtual ~LoadTimeReporter() = default;
258 
259   // Report the time it took to run android_dlopen_ext().
260   virtual void reportDlopenExtTime(int64_t milliseconds_since_boot) const = 0;
261 
262   // Report the time it took to find the RELRO region using dl_iterate_phdr().
263   virtual void reportIteratePhdrTime(int64_t milliseconds_since_boot) const = 0;
264 };
265 
266 // Holds address ranges of the loaded native library, its RELRO region, along
267 // with the RELRO FD identifying the shared memory region. Carries the same
268 // members as the Java-side LibInfo (without mLibFilePath), allowing to
269 // internally import/export the member values from/to the Java-side counterpart.
270 //
271 // Does *not* own the RELRO FD as soon as the latter gets exported to Java
272 // (as a result of 'spawning' the RELRO region as shared memory.
273 //
274 // *Not* threadsafe.
275 class NativeLibInfo {
276  public:
277   // Constructs an empty instance. The |java_object| indicates the handle to
278   // import and export member fields.
279   //
280   // Having |env| as |nullptr| disables export to java for the lifetime of the
281   // instance. This is useful as a scratch info that is gradually populated for
282   // comparison with another NativeLibInfo, and then discarded.
283   NativeLibInfo(JNIEnv* env, jobject java_object);
284 
285   // Copies the java-side object state to this native instance. Returns false
286   // iff an imported value is invalid.
287   bool CopyFromJavaObject();
288 
set_load_address(uintptr_t a)289   void set_load_address(uintptr_t a) { load_address_ = a; }
290 
load_address()291   uintptr_t load_address() const { return load_address_; }
292 
293   // Loads the native library using android_dlopen_ext and invokes JNI_OnLoad().
294   //
295   // On a successful load exports the address range of the library to the
296   // Java-side LibInfo.
297   //
298   // Iff |spawn_relro_region| is true, also finds the RELRO region in the
299   // library (PT_GNU_RELRO), converts it to be backed by a shared memory region
300   // (here referred as "RELRO FD") and exports the RELRO information to Java
301   // (the address range and the RELRO FD).
302   //
303   // When spawned, the shared memory region is exported only after sealing as
304   // read-only and without writable memory mappings. This allows any process to
305   // provide RELRO FD before it starts processing arbitrary input. For example,
306   // an App Zygote can create a RELRO FD in a sufficiently trustworthy way to
307   // make the Browser/Privileged processes share the region with it.
308   bool LoadLibrary(const String& library_path,
309                    bool spawn_relro_region,
310                    const LoadTimeReporter& reporter);
311 
312   // Finds the RELRO region in the native library identified by
313   // |this->load_address()| and replaces it with the shared memory region
314   // identified by |other_lib_info|.
315   //
316   // The external NativeLibInfo can arrive from a different process.
317   //
318   // Note on security: The RELRO region is treated as *trusted*, no untrusted
319   // user/website/network input can be processed in an isolated process before
320   // it sends the RELRO FD. This is because there is no way to check whether the
321   // process has a writable mapping of the region remaining.
322   bool CompareRelroAndReplaceItBy(const NativeLibInfo& other_lib_info);
323 
set_relro_info_for_testing(uintptr_t start,size_t size)324   void set_relro_info_for_testing(uintptr_t start, size_t size) {
325     relro_start_ = start;
326     relro_size_ = size;
327   }
328 
329   // Creates a shared RELRO region as it normally would during LoadLibrary()
330   // with |spawn_relro_region=true|. Exposed here because it is difficult to
331   // unittest LoadLibrary() directly.
332   bool CreateSharedRelroFdForTesting();
333 
set_relro_fd_for_testing(int fd)334   void set_relro_fd_for_testing(int fd) { relro_fd_ = fd; }
get_relro_fd_for_testing()335   int get_relro_fd_for_testing() const { return relro_fd_; }
get_relro_start_for_testing()336   size_t get_relro_start_for_testing() const { return relro_start_; }
get_load_size_for_testing()337   size_t get_load_size_for_testing() const { return load_size_; }
338 
339   static bool SharedMemoryFunctionsSupportedForTesting();
340 
FindRelroAndLibraryRangesInElfForTesting()341   bool FindRelroAndLibraryRangesInElfForTesting() {
342     return FindRelroAndLibraryRangesInElf();
343   }
344 
345  private:
346   NativeLibInfo() = delete;
347 
348   // Not copyable or movable.
349   NativeLibInfo(const NativeLibInfo&) = delete;
350   NativeLibInfo& operator=(const NativeLibInfo&) = delete;
351 
352   // Exports the address range of the library described by |this| to the
353   // Java-side LibInfo.
354   void ExportLoadInfoToJava() const;
355 
356   // Exports the address range of the RELRO region and RELRO FD described by
357   // |this| to the Java-side LibInfo.
358   void ExportRelroInfoToJava() const;
359 
360   void CloseRelroFd();
361 
362   // Determines the minimal address ranges for the union of all the loadable
363   // (and RELRO) segments by parsing ELF starting at |load_address()|. May fail
364   // or return incorrect results for some creative ELF libraries.
365   bool FindRelroAndLibraryRangesInElf();
366 
367   // Loads and initializes the load address ranges: |load_address_|,
368   // |load_size_|. Assumes that the memory range is reserved (in Linker.java).
369   bool LoadWithDlopenExt(const String& path,
370                          const LoadTimeReporter& reporter,
371                          void** handle);
372 
373   // Initializes |relro_fd_| with a newly created read-only shared memory region
374   // sized as the library's RELRO and with identical data.
375   bool CreateSharedRelroFd(const SharedMemoryFunctions& functions);
376 
377   // Assuming that RELRO-related information is populated, memory-maps the RELRO
378   // FD on top of the library's RELRO.
379   bool ReplaceRelroWithSharedOne(const SharedMemoryFunctions& functions) const;
380 
381   // Returns true iff the RELRO address and size, along with the contents are
382   // equal among the two.
383   bool RelroIsIdentical(const NativeLibInfo& external_lib_info,
384                         const SharedMemoryFunctions& functions) const;
385 
386   static constexpr int kInvalidFd = -1;
387   uintptr_t load_address_ = 0;
388   size_t load_size_ = 0;
389   uintptr_t relro_start_ = 0;
390   size_t relro_size_ = 0;
391   int relro_fd_ = kInvalidFd;
392   JNIEnv* const env_;
393   const jobject java_object_;
394 };
395 
396 // JNI_OnLoad() initialization hook for the linker.
397 // Sets up JNI and other initializations for native linker code.
398 // |vm| is the Java VM handle passed to JNI_OnLoad().
399 // |env| is the current JNI environment handle.
400 // On success, returns true.
401 bool LinkerJNIInit(JavaVM* vm, JNIEnv* env);
402 
403 }  // namespace chromium_android_linker
404 
405 #endif  // BASE_ANDROID_LINKER_LINKER_JNI_H_
406