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