1 // Copyright 2015 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base.library_loader; 6 7 import android.annotation.SuppressLint; 8 import android.os.Bundle; 9 import android.os.Parcel; 10 import android.os.ParcelFileDescriptor; 11 import android.os.Parcelable; 12 13 import org.chromium.base.ContextUtils; 14 import org.chromium.base.Log; 15 import org.chromium.base.StreamUtil; 16 import org.chromium.base.SysUtils; 17 import org.chromium.base.ThreadUtils; 18 import org.chromium.base.annotations.AccessedByNative; 19 import org.chromium.base.annotations.CalledByNative; 20 import org.chromium.base.annotations.MainDex; 21 22 import java.util.HashMap; 23 import java.util.Locale; 24 import java.util.Map; 25 26 import javax.annotation.Nullable; 27 28 /* 29 * Technical note: 30 * 31 * The point of this class is to provide an alternative to System.loadLibrary() 32 * to load native shared libraries. One specific feature that it supports is the 33 * ability to save RAM by sharing the ELF RELRO sections between renderer 34 * processes. 35 * 36 * When two processes load the same native library at the _same_ memory address, 37 * the content of their RELRO section (which includes C++ vtables or any 38 * constants that contain pointers) will be largely identical [1]. 39 * 40 * By default, the RELRO section is backed by private RAM in each process, 41 * which is still significant on mobile (e.g. 1.28 MB / process on Chrome 30 for 42 * Android). 43 * 44 * However, it is possible to save RAM by creating a shared memory region, 45 * copy the RELRO content into it, then have each process swap its private, 46 * regular RELRO, with a shared, read-only, mapping of the shared one. 47 * 48 * This trick saves 98% of the RELRO section size per extra process, after the 49 * first one. On the other hand, this requires careful communication between 50 * the process where the shared RELRO is created and the one(s) where it is used. 51 * 52 * Note that swapping the regular RELRO with the shared one is not an atomic 53 * operation. Care must be taken that no other thread tries to run native code 54 * that accesses it during it. In practice, this means the swap must happen 55 * before library native code is executed. 56 * 57 * [1] The exceptions are pointers to external, randomized, symbols, like 58 * those from some system libraries, but these are very few in practice. 59 */ 60 61 /* 62 * Security considerations: 63 * 64 * - Whether the browser process loads its native libraries at the same 65 * addresses as the service ones (to save RAM by sharing the RELRO too) 66 * depends on the configuration variable BROWSER_SHARED_RELRO_CONFIG. 67 * 68 * Not using fixed library addresses in the browser process is preferred 69 * for regular devices since it maintains the efficacy of ASLR as an 70 * exploit mitigation across the render <-> browser privilege boundary. 71 * 72 * - The shared RELRO memory region is always forced read-only after creation, 73 * which means it is impossible for a compromised service process to map 74 * it read-write (e.g. by calling mmap() or mprotect()) and modify its 75 * content, altering values seen in other service processes. 76 * 77 * - Once the RELRO ashmem region or file is mapped into a service process's 78 * address space, the corresponding file descriptor is immediately closed. The 79 * file descriptor is kept opened in the browser process, because a copy needs 80 * to be sent to each new potential service process. 81 * 82 * - The common library load addresses are randomized for each instance of 83 * the program on the device. See getRandomBaseLoadAddress() for more 84 * details on how this is obtained. 85 * 86 * - When loading several libraries in service processes, a simple incremental 87 * approach from the original random base load address is used. This is 88 * sufficient to deal correctly with component builds (which can use dozens 89 * of shared libraries), while regular builds always embed a single shared 90 * library per APK. 91 */ 92 93 /** 94 * Here's an explanation of how this class is supposed to be used: 95 * 96 * - Native shared libraries should be loaded with Linker.loadLibrary(), 97 * instead of System.loadLibrary(). The two functions should behave the same 98 * (at a high level). 99 * 100 * - Before loading any library, prepareLibraryLoad() should be called. 101 * 102 * - After loading all libraries, finishLibraryLoad() should be called, before 103 * running any native code from any of the libraries (except their static 104 * constructors, which can't be avoided). 105 * 106 * - A service process shall call either initServiceProcess() or 107 * disableSharedRelros() early (i.e. before any loadLibrary() call). 108 * Otherwise, the linker considers that it is running inside the browser 109 * process. This is because various Chromium projects have vastly 110 * different initialization paths. 111 * 112 * disableSharedRelros() completely disables shared RELROs, and loadLibrary() 113 * will behave exactly like System.loadLibrary(). 114 * 115 * initServiceProcess(baseLoadAddress) indicates that shared RELROs are to be 116 * used in this process. 117 * 118 * - The browser is in charge of deciding where in memory each library should 119 * be loaded. This address must be passed to each service process (see 120 * ChromiumLinkerParams.java in content for a helper class to do so). 121 * 122 * - The browser will also generate shared RELROs for each library it loads. 123 * More specifically, by default when in the browser process, the linker 124 * will: 125 * 126 * - Load libraries randomly (just like System.loadLibrary()). 127 * - Compute the fixed address to be used to load the same library 128 * in service processes. 129 * - Create a shared memory region populated with the RELRO region 130 * content pre-relocated for the specific fixed address above. 131 * 132 * Note that these shared RELRO regions cannot be used inside the browser 133 * process. They are also never mapped into it. 134 * 135 * This behaviour is altered by the BROWSER_SHARED_RELRO_CONFIG configuration 136 * variable below, which may force the browser to load the libraries at 137 * fixed addresses too. 138 * 139 * - Once all libraries are loaded in the browser process, one can call 140 * getSharedRelros() which returns a Bundle instance containing a map that 141 * links each loaded library to its shared RELRO region. 142 * 143 * This Bundle must be passed to each service process, for example through 144 * a Binder call (note that the Bundle includes file descriptors and cannot 145 * be added as an Intent extra). 146 * 147 * - In a service process, finishLibraryLoad() and/or loadLibrary() may 148 * block until the RELRO section Bundle is received. This is typically 149 * done by calling useSharedRelros() from another thread. 150 * 151 * This method also ensures the process uses the shared RELROs. 152 */ 153 public class Linker { 154 // Log tag for this class. 155 private static final String TAG = "LibraryLoader"; 156 157 // Name of the library that contains our JNI code. 158 private static final String LINKER_JNI_LIBRARY = "chromium_android_linker"; 159 160 // Constants used to control the behaviour of the browser process with 161 // regards to the shared RELRO section. 162 // NEVER -> The browser never uses it itself. 163 // LOW_RAM_ONLY -> It is only used on devices with low RAM. 164 // ALWAYS -> It is always used. 165 // NOTE: These names are known and expected by the Linker test scripts. 166 public static final int BROWSER_SHARED_RELRO_CONFIG_NEVER = 0; 167 public static final int BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY = 1; 168 public static final int BROWSER_SHARED_RELRO_CONFIG_ALWAYS = 2; 169 170 // Configuration variable used to control how the browser process uses the 171 // shared RELRO. Only change this while debugging linker-related issues. 172 // NOTE: This variable's name is known and expected by the Linker test scripts. 173 public static final int BROWSER_SHARED_RELRO_CONFIG = 174 BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY; 175 176 // Constants used to control the memory device config. Can be set explicitly 177 // by setMemoryDeviceConfigForTesting(). 178 // INIT -> Value is undetermined (will check at runtime). 179 // LOW -> This is a low-memory device. 180 // NORMAL -> This is not a low-memory device. 181 public static final int MEMORY_DEVICE_CONFIG_INIT = 0; 182 public static final int MEMORY_DEVICE_CONFIG_LOW = 1; 183 public static final int MEMORY_DEVICE_CONFIG_NORMAL = 2; 184 185 // Indicates if this is a low-memory device or not. The default is to 186 // determine this by probing the system at runtime, but this can be forced 187 // for testing by calling setMemoryDeviceConfigForTesting(). 188 private int mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_INIT; 189 190 // Set to true to enable debug logs. 191 protected static final boolean DEBUG = false; 192 193 // Used to pass the shared RELRO Bundle through Binder. 194 public static final String EXTRA_LINKER_SHARED_RELROS = 195 "org.chromium.base.android.linker.shared_relros"; 196 197 // Guards all access to the linker. 198 protected final Object mLock = new Object(); 199 200 // The name of a class that implements TestRunner. 201 private String mTestRunnerClassName; 202 203 // Size of reserved Breakpad guard region. Should match the value of 204 // kBreakpadGuardRegionBytes on the JNI side. Used when computing the load 205 // addresses of multiple loaded libraries. Set to 0 to disable the guard. 206 private static final int BREAKPAD_GUARD_REGION_BYTES = 16 * 1024 * 1024; 207 208 // Size of the area requested when using ASLR to obtain a random load address. 209 // Should match the value of kAddressSpaceReservationSize on the JNI side. 210 // Used when computing the load addresses of multiple loaded libraries to 211 // ensure that we don't try to load outside the area originally requested. 212 private static final int ADDRESS_SPACE_RESERVATION = 192 * 1024 * 1024; 213 214 // Becomes true after linker initialization. 215 private boolean mInitialized; 216 217 // Set to true if this runs in the browser process. Disabled by initServiceProcess(). 218 private boolean mInBrowserProcess = true; 219 220 // Becomes true to indicate this process needs to wait for a shared RELRO in 221 // finishLibraryLoad(). 222 private boolean mWaitForSharedRelros; 223 224 // Becomes true when initialization determines that the browser process can use the 225 // shared RELRO. 226 private boolean mBrowserUsesSharedRelro; 227 228 // The map of all RELRO sections either created or used in this process. 229 private Bundle mSharedRelros; 230 231 // Current common random base load address. A value of -1 indicates not yet initialized. 232 private long mBaseLoadAddress = -1; 233 234 // Current fixed-location load address for the next library called by loadLibrary(). 235 // A value of -1 indicates not yet initialized. 236 private long mCurrentLoadAddress = -1; 237 238 // Becomes true once prepareLibraryLoad() has been called. 239 private boolean mPrepareLibraryLoadCalled; 240 241 // The map of libraries that are currently loaded in this process. 242 private HashMap<String, LibInfo> mLoadedLibraries; 243 244 // Singleton. 245 private static final Linker sSingleton = new Linker(); 246 247 // Private singleton constructor. Linker()248 private Linker() { 249 // Ensure this class is not referenced unless it's used. 250 assert LibraryLoader.useCrazyLinker(); 251 } 252 253 /** 254 * Get singleton instance. Returns a Linker. 255 * 256 * On N+ Monochrome is selected by Play Store. With Monochrome this code is not used, instead 257 * Chrome asks the WebView to provide the library (and the shared RELRO). If the WebView fails 258 * to provide the library, the system linker is used as a fallback. 259 * 260 * Linker runs on all Android releases, but is incompatible with GVR library on N+. 261 * Linker is preferred on M- because it does not write the shared RELRO to disk at 262 * almost every cold startup. 263 * 264 * @return the Linker implementation instance. 265 */ getInstance()266 public static Linker getInstance() { 267 return sSingleton; 268 } 269 270 /** 271 * Check that native library linker tests are enabled. 272 * If not enabled, calls to testing functions will fail with an assertion 273 * error. 274 * 275 * @return true if native library linker tests are enabled. 276 */ areTestsEnabled()277 public static boolean areTestsEnabled() { 278 return NativeLibraries.sEnableLinkerTests; 279 } 280 281 /** 282 * Assert NativeLibraries.sEnableLinkerTests is true. 283 * Hard assertion that we are in a testing context. Cannot be disabled. The 284 * test methods in this module permit injection of runnable code by class 285 * name. To protect against both malicious and accidental use of these 286 * methods, we ensure that NativeLibraries.sEnableLinkerTests is true when 287 * any is called. 288 */ assertLinkerTestsAreEnabled()289 private static void assertLinkerTestsAreEnabled() { 290 assert NativeLibraries.sEnableLinkerTests : "Testing method called in non-testing context"; 291 } 292 293 /** 294 * A public interface used to run runtime linker tests after loading 295 * libraries. Should only be used to implement the linker unit tests, 296 * which is controlled by the value of NativeLibraries.sEnableLinkerTests 297 * configured at build time. 298 */ 299 public interface TestRunner { 300 /** 301 * Run runtime checks and return true if they all pass. 302 * 303 * @param memoryDeviceConfig The current memory device configuration. 304 * @param inBrowserProcess true iff this is the browser process. 305 * @return true if all checks pass. 306 */ runChecks(int memoryDeviceConfig, boolean inBrowserProcess)307 public boolean runChecks(int memoryDeviceConfig, boolean inBrowserProcess); 308 } 309 310 /** 311 * Call this to retrieve the name of the current TestRunner class name 312 * if any. This can be useful to pass it from the browser process to 313 * child ones. 314 * 315 * @return null or a String holding the name of the class implementing 316 * the TestRunner set by calling setTestRunnerClassNameForTesting() previously. 317 */ getTestRunnerClassNameForTesting()318 public final String getTestRunnerClassNameForTesting() { 319 // Sanity check. This method may only be called during tests. 320 assertLinkerTestsAreEnabled(); 321 322 synchronized (mLock) { 323 return mTestRunnerClassName; 324 } 325 } 326 327 /** 328 * Sets the test class name. 329 * 330 * On the first call, instantiates a Linker and sets its test runner class name. On subsequent 331 * calls, checks that the singleton produced by the first call matches the test runner class 332 * name. 333 */ setupForTesting(String testRunnerClassName)334 public static final void setupForTesting(String testRunnerClassName) { 335 if (DEBUG) { 336 Log.i(TAG, "setupForTesting(" + testRunnerClassName + ") called"); 337 } 338 // Sanity check. This method may only be called during tests. 339 assertLinkerTestsAreEnabled(); 340 341 synchronized (sSingleton) { 342 sSingleton.mTestRunnerClassName = testRunnerClassName; 343 } 344 } 345 346 /** 347 * Instantiate and run the current TestRunner, if any. The TestRunner implementation 348 * must be instantiated _after_ all libraries are loaded to ensure that its 349 * native methods are properly registered. 350 * 351 * @param memoryDeviceConfig Linker memory config, or 0 if unused 352 * @param inBrowserProcess true if in the browser process 353 */ runTestRunnerClassForTesting( int memoryDeviceConfig, boolean inBrowserProcess)354 private final void runTestRunnerClassForTesting( 355 int memoryDeviceConfig, boolean inBrowserProcess) { 356 if (DEBUG) { 357 Log.i(TAG, "runTestRunnerClassForTesting called"); 358 } 359 // Sanity check. This method may only be called during tests. 360 assertLinkerTestsAreEnabled(); 361 362 synchronized (mLock) { 363 if (mTestRunnerClassName == null) { 364 Log.wtf(TAG, "Linker runtime tests not set up for this process"); 365 assert false; 366 } 367 if (DEBUG) { 368 Log.i(TAG, "Instantiating " + mTestRunnerClassName); 369 } 370 TestRunner testRunner = null; 371 try { 372 testRunner = (TestRunner) Class.forName(mTestRunnerClassName) 373 .getDeclaredConstructor() 374 .newInstance(); 375 } catch (Exception e) { 376 Log.wtf(TAG, "Could not instantiate test runner class by name", e); 377 assert false; 378 } 379 380 if (!testRunner.runChecks(memoryDeviceConfig, inBrowserProcess)) { 381 Log.wtf(TAG, "Linker runtime tests failed in this process"); 382 assert false; 383 } 384 385 Log.i(TAG, "All linker tests passed"); 386 } 387 } 388 389 /** 390 * Call this method before any other Linker method to force a specific 391 * memory device configuration. Should only be used for testing. 392 * 393 * @param memoryDeviceConfig MEMORY_DEVICE_CONFIG_LOW or MEMORY_DEVICE_CONFIG_NORMAL. 394 */ setMemoryDeviceConfigForTesting(int memoryDeviceConfig)395 public final void setMemoryDeviceConfigForTesting(int memoryDeviceConfig) { 396 if (DEBUG) { 397 Log.i(TAG, "setMemoryDeviceConfigForTesting(" + memoryDeviceConfig + ") called"); 398 } 399 // Sanity check. This method may only be called during tests. 400 assertLinkerTestsAreEnabled(); 401 assert memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW 402 || memoryDeviceConfig == MEMORY_DEVICE_CONFIG_NORMAL; 403 404 synchronized (mLock) { 405 assert mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT; 406 407 mMemoryDeviceConfig = memoryDeviceConfig; 408 if (DEBUG) { 409 if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) { 410 Log.i(TAG, "Simulating a low-memory device"); 411 } else { 412 Log.i(TAG, "Simulating a regular-memory device"); 413 } 414 } 415 } 416 } 417 418 /** 419 * Determine whether a library is the linker library. 420 * 421 * @param library the name of the library. 422 * @return true is the library is the Linker's own JNI library. 423 */ isChromiumLinkerLibrary(String library)424 boolean isChromiumLinkerLibrary(String library) { 425 return library.equals(LINKER_JNI_LIBRARY); 426 } 427 428 /** 429 * Load the Linker JNI library. Throws UnsatisfiedLinkError on error. 430 */ 431 @SuppressLint({"UnsafeDynamicallyLoadedCode"}) loadLinkerJniLibrary()432 private static void loadLinkerJniLibrary() { 433 LibraryLoader.setEnvForNative(); 434 if (DEBUG) { 435 String libName = "lib" + LINKER_JNI_LIBRARY + ".so"; 436 Log.i(TAG, "Loading " + libName); 437 } 438 try { 439 System.loadLibrary(LINKER_JNI_LIBRARY); 440 LibraryLoader.incrementRelinkerCountNotHitHistogram(); 441 } catch (UnsatisfiedLinkError e) { 442 if (LibraryLoader.PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION) { 443 System.load(LibraryLoader.getExtractedLibraryPath( 444 ContextUtils.getApplicationContext(), LINKER_JNI_LIBRARY)); 445 LibraryLoader.incrementRelinkerCountHitHistogram(); 446 } 447 } 448 } 449 450 /** 451 * Obtain a random base load address at which to place loaded libraries. 452 * 453 * @return new base load address 454 */ getRandomBaseLoadAddress()455 private long getRandomBaseLoadAddress() { 456 // nativeGetRandomBaseLoadAddress() returns an address at which it has previously 457 // successfully mapped an area larger than the largest library we expect to load, 458 // on the basis that we will be able, with high probability, to map our library 459 // into it. 460 // 461 // One issue with this is that we do not yet know the size of the library that 462 // we will load is. If it is smaller than the size we used to obtain a random 463 // address the library mapping may still succeed. The other issue is that 464 // although highly unlikely, there is no guarantee that something else does not 465 // map into the area we are going to use between here and when we try to map into it. 466 // 467 // The above notes mean that all of this is probablistic. It is however okay to do 468 // because if, worst case and unlikely, we get unlucky in our choice of address, 469 // the back-out and retry without the shared RELRO in the ChildProcessService will 470 // keep things running. 471 final long address = nativeGetRandomBaseLoadAddress(); 472 if (DEBUG) { 473 Log.i(TAG, String.format(Locale.US, "Random native base load address: 0x%x", address)); 474 } 475 return address; 476 } 477 478 /** 479 * Load a native shared library with the Chromium linker. Note the crazy linker treats 480 * libraries and files as equivalent, so you can only open one library in a given zip 481 * file. The library must not be the Chromium linker library. 482 * 483 * @param libFilePath The path of the library (possibly in the zip file). 484 */ loadLibrary(String libFilePath)485 void loadLibrary(String libFilePath) { 486 if (DEBUG) { 487 Log.i(TAG, "loadLibrary: " + libFilePath); 488 } 489 final boolean isFixedAddressPermitted = true; 490 loadLibraryImpl(libFilePath, isFixedAddressPermitted); 491 } 492 493 /** 494 * Load a native shared library with the Chromium linker, ignoring any 495 * requested fixed address for RELRO sharing. Note the crazy linker treats libraries and 496 * files as equivalent, so you can only open one library in a given zip file. The 497 * library must not be the Chromium linker library. 498 * 499 * @param libFilePath The path of the library (possibly in the zip file). 500 */ loadLibraryNoFixedAddress(String libFilePath)501 void loadLibraryNoFixedAddress(String libFilePath) { 502 if (DEBUG) { 503 Log.i(TAG, "loadLibraryAtAnyAddress: " + libFilePath); 504 } 505 final boolean isFixedAddressPermitted = false; 506 loadLibraryImpl(libFilePath, isFixedAddressPermitted); 507 } 508 509 // Used internally to initialize the linker's data. Assumes lock is held. 510 // Loads JNI, and sets mMemoryDeviceConfig and mBrowserUsesSharedRelro. ensureInitializedLocked()511 private void ensureInitializedLocked() { 512 assert Thread.holdsLock(mLock); 513 514 if (mInitialized) { 515 return; 516 } 517 518 // On first call, load libchromium_android_linker.so. Cannot be done in the 519 // constructor because instantiation occurs on the UI thread. 520 loadLinkerJniLibrary(); 521 522 if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT) { 523 if (SysUtils.isLowEndDevice()) { 524 mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_LOW; 525 } else { 526 mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_NORMAL; 527 } 528 } 529 530 // Cannot run in the constructor because SysUtils.isLowEndDevice() relies 531 // on CommandLine, which may not be available at instantiation. 532 switch (BROWSER_SHARED_RELRO_CONFIG) { 533 case BROWSER_SHARED_RELRO_CONFIG_NEVER: 534 mBrowserUsesSharedRelro = false; 535 break; 536 case BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY: 537 if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) { 538 mBrowserUsesSharedRelro = true; 539 Log.w(TAG, "Low-memory device: shared RELROs used in all processes"); 540 } else { 541 mBrowserUsesSharedRelro = false; 542 } 543 break; 544 case BROWSER_SHARED_RELRO_CONFIG_ALWAYS: 545 Log.w(TAG, "Beware: shared RELROs used in all processes!"); 546 mBrowserUsesSharedRelro = true; 547 break; 548 default: 549 Log.wtf(TAG, "FATAL: illegal shared RELRO config"); 550 throw new AssertionError(); 551 } 552 553 mInitialized = true; 554 } 555 556 /** 557 * Call this method to determine if the linker will try to use shared RELROs 558 * for the browser process. 559 */ isUsingBrowserSharedRelros()560 public boolean isUsingBrowserSharedRelros() { 561 synchronized (mLock) { 562 ensureInitializedLocked(); 563 return mInBrowserProcess && mBrowserUsesSharedRelro; 564 } 565 } 566 567 /** 568 * Call this method just before loading any native shared libraries in this process. 569 * 570 * @param apkFilePath Optional current APK file path. If provided, the linker 571 * will try to load libraries directly from it. 572 */ prepareLibraryLoad(@ullable String apkFilePath)573 public void prepareLibraryLoad(@Nullable String apkFilePath) { 574 if (DEBUG) { 575 Log.i(TAG, "prepareLibraryLoad() called"); 576 } 577 synchronized (mLock) { 578 ensureInitializedLocked(); 579 if (apkFilePath != null) { 580 nativeAddZipArchivePath(apkFilePath); 581 } 582 mPrepareLibraryLoadCalled = true; 583 584 if (mInBrowserProcess) { 585 // Force generation of random base load address, as well 586 // as creation of shared RELRO sections in this process. 587 setupBaseLoadAddressLocked(); 588 } 589 } 590 } 591 592 /** 593 * Call this method just after loading all native shared libraries in this process. 594 * Note that when in a service process, this will block until the RELRO bundle is 595 * received, i.e. when another thread calls useSharedRelros(). 596 */ finishLibraryLoad()597 void finishLibraryLoad() { 598 if (DEBUG) { 599 Log.i(TAG, "finishLibraryLoad() called"); 600 } 601 synchronized (mLock) { 602 ensureInitializedLocked(); 603 if (DEBUG) { 604 Log.i(TAG, 605 String.format(Locale.US, 606 "mInBrowserProcess=%b mBrowserUsesSharedRelro=%b mWaitForSharedRelros=%b", 607 mInBrowserProcess, mBrowserUsesSharedRelro, mWaitForSharedRelros)); 608 } 609 610 if (mLoadedLibraries == null) { 611 if (DEBUG) { 612 Log.i(TAG, "No libraries loaded"); 613 } 614 } else { 615 if (mInBrowserProcess) { 616 // Create new Bundle containing RELRO section information 617 // for all loaded libraries. Make it available to getSharedRelros(). 618 mSharedRelros = createBundleFromLibInfoMap(mLoadedLibraries); 619 if (DEBUG) { 620 Log.i(TAG, "Shared RELRO created"); 621 dumpBundle(mSharedRelros); 622 } 623 624 if (mBrowserUsesSharedRelro) { 625 useSharedRelrosLocked(mSharedRelros); 626 } 627 } 628 629 if (mWaitForSharedRelros) { 630 assert !mInBrowserProcess; 631 632 // Wait until the shared relro bundle is received from useSharedRelros(). 633 while (mSharedRelros == null) { 634 try { 635 mLock.wait(); 636 } catch (InterruptedException ie) { 637 // Restore the thread's interrupt status. 638 Thread.currentThread().interrupt(); 639 } 640 } 641 useSharedRelrosLocked(mSharedRelros); 642 // Clear the Bundle to ensure its file descriptor references can't be reused. 643 mSharedRelros.clear(); 644 mSharedRelros = null; 645 } 646 } 647 648 // If testing, run tests now that all libraries are loaded and initialized. 649 if (NativeLibraries.sEnableLinkerTests) { 650 runTestRunnerClassForTesting(mMemoryDeviceConfig, mInBrowserProcess); 651 } 652 } 653 if (DEBUG) { 654 Log.i(TAG, "finishLibraryLoad() exiting"); 655 } 656 } 657 658 /** 659 * Call this to send a Bundle containing the shared RELRO sections to be 660 * used in this process. If initServiceProcess() was previously called, 661 * finishLibraryLoad() will not exit until this method is called in another 662 * thread with a non-null value. 663 * 664 * @param bundle The Bundle instance containing a map of shared RELRO sections 665 * to use in this process. 666 */ useSharedRelros(Bundle bundle)667 public void useSharedRelros(Bundle bundle) { 668 // Ensure the bundle uses the application's class loader, not the framework 669 // one which doesn't know anything about LibInfo. 670 // Also, hold a fresh copy of it so the caller can't recycle it. 671 Bundle clonedBundle = null; 672 if (bundle != null) { 673 bundle.setClassLoader(LibInfo.class.getClassLoader()); 674 clonedBundle = new Bundle(LibInfo.class.getClassLoader()); 675 Parcel parcel = Parcel.obtain(); 676 bundle.writeToParcel(parcel, 0); 677 parcel.setDataPosition(0); 678 clonedBundle.readFromParcel(parcel); 679 parcel.recycle(); 680 } 681 if (DEBUG) { 682 Log.i(TAG, "useSharedRelros() called with " + bundle + ", cloned " + clonedBundle); 683 } 684 synchronized (mLock) { 685 // Note that in certain cases, this can be called before 686 // initServiceProcess() in service processes. 687 mSharedRelros = clonedBundle; 688 // Tell any listener blocked in finishLibraryLoad() about it. 689 mLock.notifyAll(); 690 } 691 } 692 693 /** 694 * Call this to retrieve the shared RELRO sections created in this process, 695 * after loading all libraries. 696 * 697 * @return a new Bundle instance, or null if RELRO sharing is disabled on 698 * this system, or if initServiceProcess() was called previously. 699 */ getSharedRelros()700 public Bundle getSharedRelros() { 701 if (DEBUG) { 702 Log.i(TAG, "getSharedRelros() called"); 703 } 704 synchronized (mLock) { 705 if (!mInBrowserProcess) { 706 if (DEBUG) { 707 Log.i(TAG, "... returning null Bundle"); 708 } 709 return null; 710 } 711 712 // Return the Bundle created in finishLibraryLoad(). 713 if (DEBUG) { 714 Log.i(TAG, "... returning " + mSharedRelros); 715 } 716 return mSharedRelros; 717 } 718 } 719 720 /** 721 * Call this method before loading any libraries to indicate that this 722 * process shall neither create or reuse shared RELRO sections. 723 */ disableSharedRelros()724 public void disableSharedRelros() { 725 if (DEBUG) { 726 Log.i(TAG, "disableSharedRelros() called"); 727 } 728 synchronized (mLock) { 729 ensureInitializedLocked(); 730 mInBrowserProcess = false; 731 mWaitForSharedRelros = false; 732 mBrowserUsesSharedRelro = false; 733 } 734 } 735 736 /** 737 * Call this method before loading any libraries to indicate that this 738 * process is ready to reuse shared RELRO sections from another one. 739 * Typically used when starting service processes. 740 * 741 * @param baseLoadAddress the base library load address to use. 742 */ initServiceProcess(long baseLoadAddress)743 public void initServiceProcess(long baseLoadAddress) { 744 if (DEBUG) { 745 Log.i(TAG, 746 String.format(Locale.US, "initServiceProcess(0x%x) called", baseLoadAddress)); 747 } 748 synchronized (mLock) { 749 ensureInitializedLocked(); 750 mInBrowserProcess = false; 751 mBrowserUsesSharedRelro = false; 752 mWaitForSharedRelros = true; 753 mBaseLoadAddress = baseLoadAddress; 754 mCurrentLoadAddress = baseLoadAddress; 755 } 756 } 757 758 /** 759 * Retrieve the base load address of all shared RELRO sections. 760 * This also enforces the creation of shared RELRO sections in 761 * prepareLibraryLoad(), which can later be retrieved with getSharedRelros(). 762 * 763 * @return a common, random base load address, or 0 if RELRO sharing is 764 * disabled. 765 */ getBaseLoadAddress()766 public long getBaseLoadAddress() { 767 synchronized (mLock) { 768 ensureInitializedLocked(); 769 if (!mInBrowserProcess) { 770 Log.w(TAG, "Shared RELRO sections are disabled in this process!"); 771 return 0; 772 } 773 774 setupBaseLoadAddressLocked(); 775 if (DEBUG) { 776 Log.i(TAG, 777 String.format( 778 Locale.US, "getBaseLoadAddress() returns 0x%x", mBaseLoadAddress)); 779 } 780 return mBaseLoadAddress; 781 } 782 } 783 784 // Used internally to lazily setup the common random base load address. setupBaseLoadAddressLocked()785 private void setupBaseLoadAddressLocked() { 786 assert Thread.holdsLock(mLock); 787 if (mBaseLoadAddress == -1) { 788 mBaseLoadAddress = getRandomBaseLoadAddress(); 789 mCurrentLoadAddress = mBaseLoadAddress; 790 if (mBaseLoadAddress == 0) { 791 // If the random address is 0 there are issues with finding enough 792 // free address space, so disable RELRO shared / fixed load addresses. 793 Log.w(TAG, "Disabling shared RELROs due address space pressure"); 794 mBrowserUsesSharedRelro = false; 795 mWaitForSharedRelros = false; 796 } 797 } 798 } 799 800 // Used for debugging only. dumpBundle(Bundle bundle)801 private void dumpBundle(Bundle bundle) { 802 if (DEBUG) { 803 Log.i(TAG, "Bundle has " + bundle.size() + " items: " + bundle); 804 } 805 } 806 807 /** 808 * Use the shared RELRO section from a Bundle received form another process. 809 * Call this after calling setBaseLoadAddress() then loading all libraries 810 * with loadLibrary(). 811 * 812 * @param bundle Bundle instance generated with createSharedRelroBundle() in 813 * another process. 814 */ useSharedRelrosLocked(Bundle bundle)815 private void useSharedRelrosLocked(Bundle bundle) { 816 assert Thread.holdsLock(mLock); 817 818 if (DEBUG) { 819 Log.i(TAG, "Linker.useSharedRelrosLocked() called"); 820 } 821 822 if (bundle == null) { 823 if (DEBUG) { 824 Log.i(TAG, "null bundle!"); 825 } 826 return; 827 } 828 829 if (mLoadedLibraries == null) { 830 if (DEBUG) { 831 Log.i(TAG, "No libraries loaded!"); 832 } 833 return; 834 } 835 836 if (DEBUG) { 837 dumpBundle(bundle); 838 } 839 HashMap<String, LibInfo> relroMap = createLibInfoMapFromBundle(bundle); 840 841 // Apply the RELRO section to all libraries that were already loaded. 842 for (Map.Entry<String, LibInfo> entry : relroMap.entrySet()) { 843 String libName = entry.getKey(); 844 LibInfo libInfo = entry.getValue(); 845 if (!nativeUseSharedRelro(libName, libInfo)) { 846 Log.w(TAG, "Could not use shared RELRO section for " + libName); 847 } else { 848 if (DEBUG) { 849 Log.i(TAG, "Using shared RELRO section for " + libName); 850 } 851 } 852 } 853 854 // In service processes, close all file descriptors from the map now. 855 if (!mInBrowserProcess) { 856 closeLibInfoMap(relroMap); 857 } 858 859 if (DEBUG) { 860 Log.i(TAG, "Linker.useSharedRelrosLocked() exiting"); 861 } 862 } 863 864 /** 865 * Implements loading a native shared library with the Chromium linker. 866 * 867 * Load a native shared library with the Chromium linker. If the zip file 868 * is not null, the shared library must be uncompressed and page aligned 869 * inside the zipfile. Note the crazy linker treats libraries and files as 870 * equivalent, so you can only open one library in a given zip file. The 871 * library must not be the Chromium linker library. 872 * 873 * @param libFilePath The path of the library (possibly in the zip file). 874 * @param isFixedAddressPermitted If true, uses a fixed load address if one was 875 * supplied, otherwise ignores the fixed address and loads wherever available. 876 */ loadLibraryImpl(String libFilePath, boolean isFixedAddressPermitted)877 void loadLibraryImpl(String libFilePath, boolean isFixedAddressPermitted) { 878 if (DEBUG) { 879 Log.i(TAG, "loadLibraryImpl: " + libFilePath + ", " + isFixedAddressPermitted); 880 } 881 synchronized (mLock) { 882 ensureInitializedLocked(); 883 884 // Security: Ensure prepareLibraryLoad() was called before. 885 // In theory, this can be done lazily here, but it's more consistent 886 // to use a pair of functions (i.e. prepareLibraryLoad() + finishLibraryLoad()) 887 // that wrap all calls to loadLibrary() in the library loader. 888 assert mPrepareLibraryLoadCalled; 889 890 if (mLoadedLibraries == null) { 891 mLoadedLibraries = new HashMap<String, LibInfo>(); 892 } 893 894 if (mLoadedLibraries.containsKey(libFilePath)) { 895 if (DEBUG) { 896 Log.i(TAG, "Not loading " + libFilePath + " twice"); 897 } 898 return; 899 } 900 901 LibInfo libInfo = new LibInfo(); 902 long loadAddress = 0; 903 if (isFixedAddressPermitted) { 904 if ((mInBrowserProcess && mBrowserUsesSharedRelro) || mWaitForSharedRelros) { 905 // Load the library at a fixed address. 906 loadAddress = mCurrentLoadAddress; 907 908 // For multiple libraries, ensure we stay within reservation range. 909 if (loadAddress > mBaseLoadAddress + ADDRESS_SPACE_RESERVATION) { 910 String errorMessage = 911 "Load address outside reservation, for: " + libFilePath; 912 Log.e(TAG, errorMessage); 913 throw new UnsatisfiedLinkError(errorMessage); 914 } 915 } 916 } 917 918 final String sharedRelRoName = libFilePath; 919 if (!nativeLoadLibrary(libFilePath, loadAddress, libInfo)) { 920 String errorMessage = "Unable to load library: " + libFilePath; 921 Log.e(TAG, errorMessage); 922 throw new UnsatisfiedLinkError(errorMessage); 923 } 924 925 // Print the load address to the logcat when testing the linker. The format 926 // of the string is expected by the Python test_runner script as one of: 927 // BROWSER_LIBRARY_ADDRESS: <library-name> <address> 928 // RENDERER_LIBRARY_ADDRESS: <library-name> <address> 929 // Where <library-name> is the library name, and <address> is the hexadecimal load 930 // address. 931 if (NativeLibraries.sEnableLinkerTests) { 932 String tag = 933 mInBrowserProcess ? "BROWSER_LIBRARY_ADDRESS" : "RENDERER_LIBRARY_ADDRESS"; 934 Log.i(TAG, 935 String.format( 936 Locale.US, "%s: %s %x", tag, libFilePath, libInfo.mLoadAddress)); 937 } 938 939 if (mInBrowserProcess) { 940 // Create a new shared RELRO section at the 'current' fixed load address. 941 if (!nativeCreateSharedRelro(sharedRelRoName, mCurrentLoadAddress, libInfo)) { 942 Log.w(TAG, 943 String.format(Locale.US, "Could not create shared RELRO for %s at %x", 944 libFilePath, mCurrentLoadAddress)); 945 } else { 946 if (DEBUG) { 947 Log.i(TAG, 948 String.format(Locale.US, "Created shared RELRO for %s at %x: %s", 949 sharedRelRoName, mCurrentLoadAddress, libInfo.toString())); 950 } 951 } 952 } 953 954 if (loadAddress != 0 && mCurrentLoadAddress != 0) { 955 // Compute the next current load address. If mCurrentLoadAddress 956 // is not 0, this is an explicit library load address. Otherwise, 957 // this is an explicit load address for relocated RELRO sections 958 // only. 959 mCurrentLoadAddress = 960 libInfo.mLoadAddress + libInfo.mLoadSize + BREAKPAD_GUARD_REGION_BYTES; 961 } 962 963 mLoadedLibraries.put(sharedRelRoName, libInfo); 964 if (DEBUG) { 965 Log.i(TAG, "Library details " + libInfo.toString()); 966 } 967 } 968 } 969 970 /** 971 * Record information for a given library. 972 * IMPORTANT: Native code knows about this class's fields, so 973 * don't change them without modifying the corresponding C++ sources. 974 * Also, the LibInfo instance owns the shared RELRO file descriptor. 975 */ 976 private static class LibInfo implements Parcelable { LibInfo()977 LibInfo() {} 978 979 // from Parcelable LibInfo(Parcel in)980 LibInfo(Parcel in) { 981 mLoadAddress = in.readLong(); 982 mLoadSize = in.readLong(); 983 mRelroStart = in.readLong(); 984 mRelroSize = in.readLong(); 985 ParcelFileDescriptor fd = ParcelFileDescriptor.CREATOR.createFromParcel(in); 986 // If CreateSharedRelro fails, the OS file descriptor will be -1 and |fd| will be null. 987 if (fd != null) { 988 mRelroFd = fd.detachFd(); 989 } 990 } 991 close()992 public void close() { 993 if (mRelroFd >= 0) { 994 StreamUtil.closeQuietly(ParcelFileDescriptor.adoptFd(mRelroFd)); 995 mRelroFd = -1; 996 } 997 } 998 999 // from Parcelable 1000 @Override writeToParcel(Parcel out, int flags)1001 public void writeToParcel(Parcel out, int flags) { 1002 if (mRelroFd >= 0) { 1003 out.writeLong(mLoadAddress); 1004 out.writeLong(mLoadSize); 1005 out.writeLong(mRelroStart); 1006 out.writeLong(mRelroSize); 1007 try { 1008 ParcelFileDescriptor fd = ParcelFileDescriptor.fromFd(mRelroFd); 1009 fd.writeToParcel(out, 0); 1010 fd.close(); 1011 } catch (java.io.IOException e) { 1012 Log.e(TAG, "Can't write LibInfo file descriptor to parcel", e); 1013 } 1014 } 1015 } 1016 1017 // from Parcelable 1018 @Override describeContents()1019 public int describeContents() { 1020 return Parcelable.CONTENTS_FILE_DESCRIPTOR; 1021 } 1022 1023 // from Parcelable 1024 public static final Parcelable.Creator<LibInfo> CREATOR = 1025 new Parcelable.Creator<LibInfo>() { 1026 @Override 1027 public LibInfo createFromParcel(Parcel in) { 1028 return new LibInfo(in); 1029 } 1030 1031 @Override 1032 public LibInfo[] newArray(int size) { 1033 return new LibInfo[size]; 1034 } 1035 }; 1036 1037 // IMPORTANT: Don't change these fields without modifying the 1038 // native code that accesses them directly! 1039 @AccessedByNative 1040 public long mLoadAddress; // page-aligned library load address. 1041 @AccessedByNative 1042 public long mLoadSize; // page-aligned library load size. 1043 @AccessedByNative 1044 public long mRelroStart; // page-aligned address in memory, or 0 if none. 1045 @AccessedByNative 1046 public long mRelroSize; // page-aligned size in memory, or 0. 1047 @AccessedByNative 1048 public int mRelroFd = -1; // shared RELRO file descriptor, or -1 1049 } 1050 1051 // Create a Bundle from a map of LibInfo objects. createBundleFromLibInfoMap(HashMap<String, LibInfo> map)1052 private Bundle createBundleFromLibInfoMap(HashMap<String, LibInfo> map) { 1053 Bundle bundle = new Bundle(map.size()); 1054 for (Map.Entry<String, LibInfo> entry : map.entrySet()) { 1055 bundle.putParcelable(entry.getKey(), entry.getValue()); 1056 } 1057 return bundle; 1058 } 1059 1060 // Create a new LibInfo map from a Bundle. createLibInfoMapFromBundle(Bundle bundle)1061 private HashMap<String, LibInfo> createLibInfoMapFromBundle(Bundle bundle) { 1062 HashMap<String, LibInfo> map = new HashMap<String, LibInfo>(); 1063 for (String library : bundle.keySet()) { 1064 LibInfo libInfo = bundle.getParcelable(library); 1065 map.put(library, libInfo); 1066 } 1067 return map; 1068 } 1069 1070 // Call the close() method on all values of a LibInfo map. closeLibInfoMap(HashMap<String, LibInfo> map)1071 private void closeLibInfoMap(HashMap<String, LibInfo> map) { 1072 for (Map.Entry<String, LibInfo> entry : map.entrySet()) { 1073 entry.getValue().close(); 1074 } 1075 } 1076 1077 /** 1078 * Move activity from the native thread to the main UI thread. 1079 * Called from native code on its own thread. Posts a callback from 1080 * the UI thread back to native code. 1081 * 1082 * @param opaque Opaque argument. 1083 */ 1084 @CalledByNative 1085 @MainDex postCallbackOnMainThread(final long opaque)1086 private static void postCallbackOnMainThread(final long opaque) { 1087 ThreadUtils.postOnUiThread(new Runnable() { 1088 @Override 1089 public void run() { 1090 nativeRunCallbackOnUiThread(opaque); 1091 } 1092 }); 1093 } 1094 1095 /** 1096 * Native method to run callbacks on the main UI thread. 1097 * Supplied by the crazy linker and called by postCallbackOnMainThread. 1098 * 1099 * @param opaque Opaque crazy linker arguments. 1100 */ nativeRunCallbackOnUiThread(long opaque)1101 private static native void nativeRunCallbackOnUiThread(long opaque); 1102 1103 /** 1104 * Native method used to load a library. 1105 * 1106 * @param library Platform specific library name (e.g. libfoo.so) 1107 * @param loadAddress Explicit load address, or 0 for randomized one. 1108 * @param libInfo If not null, the mLoadAddress and mLoadSize fields 1109 * of this LibInfo instance will set on success. 1110 * @return true for success, false otherwise. 1111 */ nativeLoadLibrary( String library, long loadAddress, LibInfo libInfo)1112 private static native boolean nativeLoadLibrary( 1113 String library, long loadAddress, LibInfo libInfo); 1114 1115 /** 1116 * Native method used to add a zip archive or APK to the search path 1117 * for native libraries. Allows loading directly from it. 1118 * 1119 * @param zipfilePath Path of the zip file containing the libraries. 1120 * @return true for success, false otherwise. 1121 */ nativeAddZipArchivePath(String zipFilePath)1122 private static native boolean nativeAddZipArchivePath(String zipFilePath); 1123 1124 /** 1125 * Native method used to create a shared RELRO section. 1126 * If the library was already loaded at the same address using 1127 * nativeLoadLibrary(), this creates the RELRO for it. Otherwise, 1128 * this loads a new temporary library at the specified address, 1129 * creates and extracts the RELRO section from it, then unloads it. 1130 * 1131 * @param library Library name. 1132 * @param loadAddress load address, which can be different from the one 1133 * used to load the library in the current process! 1134 * @param libInfo libInfo instance. On success, the mRelroStart, mRelroSize 1135 * and mRelroFd will be set. 1136 * @return true on success, false otherwise. 1137 */ nativeCreateSharedRelro( String library, long loadAddress, LibInfo libInfo)1138 private static native boolean nativeCreateSharedRelro( 1139 String library, long loadAddress, LibInfo libInfo); 1140 1141 /** 1142 * Native method used to use a shared RELRO section. 1143 * 1144 * @param library Library name. 1145 * @param libInfo A LibInfo instance containing valid RELRO information 1146 * @return true on success. 1147 */ nativeUseSharedRelro(String library, LibInfo libInfo)1148 private static native boolean nativeUseSharedRelro(String library, LibInfo libInfo); 1149 1150 /** 1151 * Return a random address that should be free to be mapped with the given size. 1152 * Maps an area large enough for the largest library we might attempt to load, 1153 * and if successful then unmaps it and returns the address of the area allocated 1154 * by the system (with ASLR). The idea is that this area should remain free of 1155 * other mappings until we map our library into it. 1156 * 1157 * @return address to pass to future mmap, or 0 on error. 1158 */ nativeGetRandomBaseLoadAddress()1159 private static native long nativeGetRandomBaseLoadAddress(); 1160 } 1161