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