| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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.content.app; | |
| 6 | |
| 7 import android.os.Bundle; | |
| 8 import android.os.Parcel; | |
| 9 import android.os.ParcelFileDescriptor; | |
| 10 import android.os.Parcelable; | |
| 11 import android.util.Log; | |
| 12 | |
| 13 import org.chromium.base.SysUtils; | |
| 14 | |
| 15 import java.io.File; | |
| 16 import java.io.FileInputStream; | |
| 17 import java.util.HashMap; | |
| 18 import java.util.Map; | |
| 19 | |
| 20 /* | |
| 21 * Technical note: | |
| 22 * | |
| 23 * The point of this class is to provide an alternative to System.loadLibrary() | |
| 24 * to load native shared libraries. One specific feature that it supports is the | |
| 25 * ability to save RAM by sharing the ELF RELRO sections between renderer | |
| 26 * processes. | |
| 27 * | |
| 28 * When two processes load the same native library at the _same_ memory address, | |
| 29 * the content of their RELRO section (which includes C++ vtables or any | |
| 30 * constants that contain pointers) will be largely identical [1]. | |
| 31 * | |
| 32 * By default, the RELRO section is backed by private RAM in each process, | |
| 33 * which is still significant on mobile (e.g. 1.28 MB / process on Chrome 30 for | |
| 34 * Android). | |
| 35 * | |
| 36 * However, it is possible to save RAM by creating a shared memory region, | |
| 37 * copy the RELRO content into it, then have each process swap its private, | |
| 38 * regular RELRO, with a shared, read-only, mapping of the shared one. | |
| 39 * | |
| 40 * This trick saves 98% of the RELRO section size per extra process, after the | |
| 41 * first one. On the other hand, this requires careful communication between | |
| 42 * the process where the shared RELRO is created and the one(s) where it is used
. | |
| 43 * | |
| 44 * Note that swapping the regular RELRO with the shared one is not an atomic | |
| 45 * operation. Care must be taken that no other thread tries to run native code | |
| 46 * that accesses it during it. In practice, this means the swap must happen | |
| 47 * before library native code is executed. | |
| 48 * | |
| 49 * [1] The exceptions are pointers to external, randomized, symbols, like | |
| 50 * those from some system libraries, but these are very few in practice. | |
| 51 */ | |
| 52 | |
| 53 /* | |
| 54 * Security considerations: | |
| 55 * | |
| 56 * - Whether the browser process loads its native libraries at the same | |
| 57 * addresses as the service ones (to save RAM by sharing the RELRO too) | |
| 58 * depends on the configuration variable BROWSER_SHARED_RELRO_CONFIG below. | |
| 59 * | |
| 60 * Not using fixed library addresses in the browser process is preferred | |
| 61 * for regular devices since it maintains the efficacy of ASLR as an | |
| 62 * exploit mitigation across the render <-> browser privilege boundary. | |
| 63 * | |
| 64 * - The shared RELRO memory region is always forced read-only after creation, | |
| 65 * which means it is impossible for a compromised service process to map | |
| 66 * it read-write (e.g. by calling mmap() or mprotect()) and modify its | |
| 67 * content, altering values seen in other service processes. | |
| 68 * | |
| 69 * - Unfortunately, certain Android systems use an old, buggy kernel, that | |
| 70 * doesn't check Ashmem region permissions correctly. See CVE-2011-1149 | |
| 71 * for details. This linker probes the system on startup and will completely | |
| 72 * disable shared RELROs if it detects the problem. For the record, this is | |
| 73 * common for Android emulator system images (which are still based on 2.6.29) | |
| 74 * | |
| 75 * - Once the RELRO ashmem region is mapped into a service process' address | |
| 76 * space, the corresponding file descriptor is immediately closed. The | |
| 77 * file descriptor is kept opened in the browser process, because a copy needs | |
| 78 * to be sent to each new potential service process. | |
| 79 * | |
| 80 * - The common library load addresses are randomized for each instance of | |
| 81 * the program on the device. See computeRandomBaseLoadAddress() for more | |
| 82 * details on how this is computed. | |
| 83 * | |
| 84 * - When loading several libraries in service processes, a simple incremental | |
| 85 * approach from the original random base load address is used. This is | |
| 86 * sufficient to deal correctly with component builds (which can use dozens | |
| 87 * of shared libraries), while regular builds always embed a single shared | |
| 88 * library per APK. | |
| 89 */ | |
| 90 | |
| 91 /** | |
| 92 * Here's an explanation of how this class is supposed to be used: | |
| 93 * | |
| 94 * - Native shared libraries should be loaded with Linker.loadLibrary(), | |
| 95 * instead of System.loadLibrary(). The two functions take the same parameter | |
| 96 * and should behave the same (at a high level). | |
| 97 * | |
| 98 * - Before loading any library, prepareLibraryLoad() should be called. | |
| 99 * | |
| 100 * - After loading all libraries, finishLibraryLoad() should be called, before | |
| 101 * running any native code from any of the libraries (except their static | |
| 102 * constructors, which can't be avoided). | |
| 103 * | |
| 104 * - A service process shall call either initServiceProcess() or | |
| 105 * disableSharedRelros() early (i.e. before any loadLibrary() call). | |
| 106 * Otherwise, the linker considers that it is running inside the browser | |
| 107 * process. This is because various content-based projects have vastly | |
| 108 * different initialization paths. | |
| 109 * | |
| 110 * disableSharedRelros() completely disables shared RELROs, and loadLibrary() | |
| 111 * will behave exactly like System.loadLibrary(). | |
| 112 * | |
| 113 * initServiceProcess(baseLoadAddress) indicates that shared RELROs are to be | |
| 114 * used in this process. | |
| 115 * | |
| 116 * - The browser is in charge of deciding where in memory each library should | |
| 117 * be loaded. This address must be passed to each service process (see | |
| 118 * LinkerParams.java for a helper class to do so). | |
| 119 * | |
| 120 * - The browser will also generate shared RELROs for each library it loads. | |
| 121 * More specifically, by default when in the browser process, the linker | |
| 122 * will: | |
| 123 * | |
| 124 * - Load libraries randomly (just like System.loadLibrary()). | |
| 125 * - Compute the fixed address to be used to load the same library | |
| 126 * in service processes. | |
| 127 * - Create a shared memory region populated with the RELRO region | |
| 128 * content pre-relocated for the specific fixed address above. | |
| 129 * | |
| 130 * Note that these shared RELRO regions cannot be used inside the browser | |
| 131 * process. They are also never mapped into it. | |
| 132 * | |
| 133 * This behaviour is altered by the BROWSER_SHARED_RELRO_CONFIG configuration | |
| 134 * variable below, which may force the browser to load the libraries at | |
| 135 * fixed addresses to. | |
| 136 * | |
| 137 * - Once all libraries are loaded in the browser process, one can call | |
| 138 * getSharedRelros() which returns a Bundle instance containing a map that | |
| 139 * links each loaded library to its shared RELRO region. | |
| 140 * | |
| 141 * This Bundle must be passed to each service process, for example through | |
| 142 * a Binder call (note that the Bundle includes file descriptors and cannot | |
| 143 * be added as an Intent extra). | |
| 144 * | |
| 145 * - In a service process, finishLibraryLoad() will block until the RELRO | |
| 146 * section Bundle is received. This is typically done by calling | |
| 147 * useSharedRelros() from another thread. | |
| 148 * | |
| 149 * This method also ensures the process uses the shared RELROs. | |
| 150 */ | |
| 151 public class Linker { | |
| 152 | |
| 153 // Log tag for this class. This must match the name of the linker's native l
ibrary. | |
| 154 private static final String TAG = "content_android_linker"; | |
| 155 | |
| 156 // Set to true to enable debug logs. | |
| 157 private static final boolean DEBUG = false; | |
| 158 | |
| 159 // Constants used to control the behaviour of the browser process with | |
| 160 // regards to the shared RELRO section. | |
| 161 // NEVER -> The browser never uses it itself. | |
| 162 // LOW_RAM_ONLY -> It is only used on devices with low RAM. | |
| 163 // ALWAYS -> It is always used. | |
| 164 // NOTE: These names are known and expected by the Linker test scripts. | |
| 165 public static final int BROWSER_SHARED_RELRO_CONFIG_NEVER = 0; | |
| 166 public static final int BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY = 1; | |
| 167 public static final int BROWSER_SHARED_RELRO_CONFIG_ALWAYS = 2; | |
| 168 | |
| 169 // Configuration variable used to control how the browser process uses the | |
| 170 // shared RELRO. Only change this while debugging linker-related issues. | |
| 171 // NOTE: This variable's name is known and expected by the Linker test scrip
ts. | |
| 172 public static final int BROWSER_SHARED_RELRO_CONFIG = | |
| 173 BROWSER_SHARED_RELRO_CONFIG_ALWAYS; | |
| 174 | |
| 175 // Constants used to control the value of sMemoryDeviceConfig. | |
| 176 // INIT -> Value is undetermined (will check at runtime). | |
| 177 // LOW -> This is a low-memory device. | |
| 178 // NORMAL -> This is not a low-memory device. | |
| 179 public static final int MEMORY_DEVICE_CONFIG_INIT = 0; | |
| 180 public static final int MEMORY_DEVICE_CONFIG_LOW = 1; | |
| 181 public static final int MEMORY_DEVICE_CONFIG_NORMAL = 2; | |
| 182 | |
| 183 // Indicates if this is a low-memory device or not. The default is to | |
| 184 // determine this by probing the system at runtime, but this can be forced | |
| 185 // for testing by calling setMemoryDeviceConfig(). | |
| 186 private static int sMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_INIT; | |
| 187 | |
| 188 // Becomes true after linker initialization. | |
| 189 private static boolean sInitialized = false; | |
| 190 | |
| 191 // Set to true to indicate that the system supports safe sharing of RELRO se
ctions. | |
| 192 private static boolean sRelroSharingSupported = false; | |
| 193 | |
| 194 // Set to true if this runs in the browser process. Disabled by initServiceP
rocess(). | |
| 195 private static boolean sInBrowserProcess = true; | |
| 196 | |
| 197 // Becomes true to indicate this process needs to wait for a shared RELRO in | |
| 198 // finishLibraryLoad(). | |
| 199 private static boolean sWaitForSharedRelros = false; | |
| 200 | |
| 201 // Becomes true when initialization determines that the browser process can
use the | |
| 202 // shared RELRO. | |
| 203 private static boolean sBrowserUsesSharedRelro = false; | |
| 204 | |
| 205 // The map of all RELRO sections either created or used in this process. | |
| 206 private static Bundle sSharedRelros = null; | |
| 207 | |
| 208 // Current common random base load address. | |
| 209 private static long sBaseLoadAddress = 0; | |
| 210 | |
| 211 // Current fixed-location load address for the next library called by loadLi
brary(). | |
| 212 private static long sCurrentLoadAddress = 0; | |
| 213 | |
| 214 // Becomes true if any library fails to load at a given, non-0, fixed addres
s. | |
| 215 private static boolean sLoadAtFixedAddressFailed = false; | |
| 216 | |
| 217 // Becomes true once prepareLibraryLoad() has been called. | |
| 218 private static boolean sPrepareLibraryLoadCalled = false; | |
| 219 | |
| 220 // Used internally to initialize the linker's static data. Assume lock is he
ld. | |
| 221 private static void ensureInitializedLocked() { | |
| 222 assert Thread.holdsLock(Linker.class); | |
| 223 | |
| 224 if (!sInitialized) { | |
| 225 sRelroSharingSupported = false; | |
| 226 if (NativeLibraries.USE_LINKER) { | |
| 227 if (DEBUG) Log.i(TAG, "Loading lib" + TAG + ".so"); | |
| 228 try { | |
| 229 System.loadLibrary(TAG); | |
| 230 } catch (UnsatisfiedLinkError e) { | |
| 231 // In a component build, the ".cr" suffix is added to each l
ibrary name. | |
| 232 System.loadLibrary(TAG + ".cr"); | |
| 233 } | |
| 234 sRelroSharingSupported = nativeCanUseSharedRelro(); | |
| 235 if (!sRelroSharingSupported) | |
| 236 Log.w(TAG, "This system cannot safely share RELRO sections")
; | |
| 237 else { | |
| 238 if (DEBUG) Log.i(TAG, "This system supports safe shared RELR
O sections"); | |
| 239 } | |
| 240 | |
| 241 if (sMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT) { | |
| 242 sMemoryDeviceConfig = SysUtils.isLowEndDevice() ? | |
| 243 MEMORY_DEVICE_CONFIG_LOW : MEMORY_DEVICE_CONFIG_NORM
AL; | |
| 244 } | |
| 245 | |
| 246 switch (BROWSER_SHARED_RELRO_CONFIG) { | |
| 247 case BROWSER_SHARED_RELRO_CONFIG_NEVER: | |
| 248 sBrowserUsesSharedRelro = false; | |
| 249 break; | |
| 250 case BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY: | |
| 251 sBrowserUsesSharedRelro = | |
| 252 (sMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW
); | |
| 253 if (sBrowserUsesSharedRelro) | |
| 254 Log.w(TAG, "Low-memory device: shared RELROs used in
all processes"); | |
| 255 break; | |
| 256 case BROWSER_SHARED_RELRO_CONFIG_ALWAYS: | |
| 257 Log.w(TAG, "Beware: shared RELROs used in all processes!
"); | |
| 258 sBrowserUsesSharedRelro = true; | |
| 259 break; | |
| 260 default: | |
| 261 assert false : "Unreached"; | |
| 262 break; | |
| 263 } | |
| 264 } else { | |
| 265 if (DEBUG) Log.i(TAG, "Linker disabled"); | |
| 266 } | |
| 267 | |
| 268 if (!sRelroSharingSupported) { | |
| 269 // Sanity. | |
| 270 sBrowserUsesSharedRelro = false; | |
| 271 sWaitForSharedRelros = false; | |
| 272 } | |
| 273 | |
| 274 sInitialized = true; | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 /** | |
| 279 * A public interface used to run runtime linker tests after loading | |
| 280 * libraries. Should only be used to implement the linker unit tests, | |
| 281 * which is controlled by the value of NativeLibraries.ENABLE_LINKER_TESTS | |
| 282 * configured at build time. | |
| 283 */ | |
| 284 public interface TestRunner { | |
| 285 /** | |
| 286 * Run runtime checks and return true if they all pass. | |
| 287 * @param memoryDeviceConfig The current memory device configuration. | |
| 288 * @param inBrowserProcess true iff this is the browser process. | |
| 289 */ | |
| 290 public boolean runChecks(int memoryDeviceConfig, boolean inBrowserProces
s); | |
| 291 } | |
| 292 | |
| 293 // The name of a class that implements TestRunner. | |
| 294 static String sTestRunnerClassName = null; | |
| 295 | |
| 296 /** | |
| 297 * Set the TestRunner by its class name. It will be instantiated at | |
| 298 * runtime after all libraries are loaded. | |
| 299 * @param testRunnerClassName null or a String for the class name of the | |
| 300 * TestRunner to use. | |
| 301 */ | |
| 302 public static void setTestRunnerClassName(String testRunnerClassName) { | |
| 303 if (DEBUG) Log.i(TAG, "setTestRunnerByClassName(" + testRunnerClassName
+ ") called"); | |
| 304 | |
| 305 if (!NativeLibraries.ENABLE_LINKER_TESTS) { | |
| 306 // Ignore this in production code to prevent malvolent runtime injec
tion. | |
| 307 return; | |
| 308 } | |
| 309 | |
| 310 synchronized (Linker.class) { | |
| 311 assert sTestRunnerClassName == null; | |
| 312 sTestRunnerClassName = testRunnerClassName; | |
| 313 } | |
| 314 } | |
| 315 | |
| 316 /** | |
| 317 * Call this to retrieve the name of the current TestRunner class name | |
| 318 * if any. This can be useful to pass it from the browser process to | |
| 319 * child ones. | |
| 320 * @return null or a String holding the name of the class implementing | |
| 321 * the TestRunner set by calling setTestRunnerClassName() previously. | |
| 322 */ | |
| 323 public static String getTestRunnerClassName() { | |
| 324 synchronized (Linker.class) { | |
| 325 return sTestRunnerClassName; | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 /** | |
| 330 * Call this method before any other Linker method to force a specific | |
| 331 * memory device configuration. Should only be used for testing. | |
| 332 * @param memoryDeviceConfig either MEMORY_DEVICE_CONFIG_LOW or MEMORY_DEVIC
E_CONFIG_NORMAL. | |
| 333 */ | |
| 334 public static void setMemoryDeviceConfig(int memoryDeviceConfig) { | |
| 335 if (DEBUG) Log.i(TAG, "setMemoryDeviceConfig(" + memoryDeviceConfig + ")
called"); | |
| 336 // Sanity check. This method should only be called during tests. | |
| 337 assert NativeLibraries.ENABLE_LINKER_TESTS; | |
| 338 synchronized (Linker.class) { | |
| 339 assert sMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT; | |
| 340 assert memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW || | |
| 341 memoryDeviceConfig == MEMORY_DEVICE_CONFIG_NORMAL; | |
| 342 if (DEBUG) { | |
| 343 if (memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) | |
| 344 Log.i(TAG, "Simulating a low-memory device"); | |
| 345 else | |
| 346 Log.i(TAG, "Simulating a regular-memory device"); | |
| 347 } | |
| 348 sMemoryDeviceConfig = memoryDeviceConfig; | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 /** | |
| 353 * Call this method to determine if this content-based project must | |
| 354 * use this linker. If not, System.loadLibrary() should be used to load | |
| 355 * libraries instead. | |
| 356 */ | |
| 357 public static boolean isUsed() { | |
| 358 // Only GYP targets that are APKs and have the 'use_content_linker' vari
able | |
| 359 // defined as 1 will use this linker. For all others (the default), the | |
| 360 // auto-generated NativeLibraries.USE_LINKER variable will be false. | |
| 361 if (!NativeLibraries.USE_LINKER) | |
| 362 return false; | |
| 363 | |
| 364 synchronized (Linker.class) { | |
| 365 ensureInitializedLocked(); | |
| 366 // At the moment, there is also no point in using this linker if the | |
| 367 // system does not support RELRO sharing safely. | |
| 368 return sRelroSharingSupported; | |
| 369 } | |
| 370 } | |
| 371 | |
| 372 /** | |
| 373 * Call this method just before loading any native shared libraries in this
process. | |
| 374 */ | |
| 375 public static void prepareLibraryLoad() { | |
| 376 if (DEBUG) Log.i(TAG, "prepareLibraryLoad() called"); | |
| 377 synchronized (Linker.class) { | |
| 378 sPrepareLibraryLoadCalled = true; | |
| 379 | |
| 380 if (sInBrowserProcess) { | |
| 381 // Force generation of random base load address, as well | |
| 382 // as creation of shared RELRO sections in this process. | |
| 383 setupBaseLoadAddressLocked(); | |
| 384 } | |
| 385 } | |
| 386 } | |
| 387 | |
| 388 /** | |
| 389 * Call this method just after loading all native shared libraries in this p
rocess. | |
| 390 * Note that when in a service process, this will block until the RELRO bund
le is | |
| 391 * received, i.e. when another thread calls useSharedRelros(). | |
| 392 */ | |
| 393 public static void finishLibraryLoad() { | |
| 394 if (DEBUG) Log.i(TAG, "finishLibraryLoad() called"); | |
| 395 synchronized (Linker.class) { | |
| 396 if (DEBUG) Log.i(TAG, String.format( | |
| 397 "sInBrowserProcess=%s sBrowserUsesSharedRelro=%s sWaitForSha
redRelros=%s", | |
| 398 sInBrowserProcess ? "true" : "false", | |
| 399 sBrowserUsesSharedRelro ? "true" : "false", | |
| 400 sWaitForSharedRelros ? "true" : "false")); | |
| 401 | |
| 402 if (sLoadedLibraries == null) { | |
| 403 if (DEBUG) Log.i(TAG, "No libraries loaded"); | |
| 404 } else { | |
| 405 if (sInBrowserProcess) { | |
| 406 // Create new Bundle containing RELRO section information | |
| 407 // for all loaded libraries. Make it available to getSharedR
elros(). | |
| 408 sSharedRelros = createBundleFromLibInfoMap(sLoadedLibraries)
; | |
| 409 if (DEBUG) { | |
| 410 Log.i(TAG, "Shared RELRO created"); | |
| 411 dumpBundle(sSharedRelros); | |
| 412 } | |
| 413 | |
| 414 if (sBrowserUsesSharedRelro) { | |
| 415 useSharedRelrosLocked(sSharedRelros); | |
| 416 } | |
| 417 } | |
| 418 | |
| 419 if (sWaitForSharedRelros) { | |
| 420 assert !sInBrowserProcess; | |
| 421 | |
| 422 // Wait until the shared relro bundle is received from useSh
aredRelros(). | |
| 423 while (sSharedRelros == null) { | |
| 424 try { | |
| 425 Linker.class.wait(); | |
| 426 } catch (InterruptedException ie) { | |
| 427 } | |
| 428 } | |
| 429 useSharedRelrosLocked(sSharedRelros); | |
| 430 // Clear the Bundle to ensure its file descriptor references
can't be reused. | |
| 431 sSharedRelros.clear(); | |
| 432 sSharedRelros = null; | |
| 433 } | |
| 434 } | |
| 435 | |
| 436 if (NativeLibraries.ENABLE_LINKER_TESTS && sTestRunnerClassName != n
ull) { | |
| 437 // The TestRunner implementation must be instantiated _after_ | |
| 438 // all libraries are loaded to ensure that its native methods | |
| 439 // are properly registered. | |
| 440 if (DEBUG) Log.i(TAG, "Instantiating " + sTestRunnerClassName); | |
| 441 TestRunner testRunner = null; | |
| 442 try { | |
| 443 testRunner = (TestRunner) | |
| 444 Class.forName(sTestRunnerClassName).newInstance(); | |
| 445 } catch (Exception e) { | |
| 446 Log.e(TAG, "Could not extract test runner class name", e); | |
| 447 testRunner = null; | |
| 448 } | |
| 449 if (testRunner != null) { | |
| 450 if (!testRunner.runChecks(sMemoryDeviceConfig, sInBrowserPro
cess)) { | |
| 451 Log.wtf(TAG, "Linker runtime tests failed in this proces
s!!"); | |
| 452 assert false; | |
| 453 } else { | |
| 454 Log.i(TAG, "All linker tests passed!"); | |
| 455 } | |
| 456 } | |
| 457 } | |
| 458 } | |
| 459 if (DEBUG) Log.i(TAG, "finishLibraryLoad() exiting"); | |
| 460 } | |
| 461 | |
| 462 /** | |
| 463 * Call this to send a Bundle containing the shared RELRO sections to be | |
| 464 * used in this process. If initServiceProcess() was previously called, | |
| 465 * finishLibraryLoad() will not exit until this method is called in another | |
| 466 * thread with a non-null value. | |
| 467 * @param bundle The Bundle instance containing a map of shared RELRO sectio
ns | |
| 468 * to use in this process. | |
| 469 */ | |
| 470 public static void useSharedRelros(Bundle bundle) { | |
| 471 // Ensure the bundle uses the application's class loader, not the framew
ork | |
| 472 // one which doesn't know anything about LibInfo. | |
| 473 if (bundle != null) | |
| 474 bundle.setClassLoader(LibInfo.class.getClassLoader()); | |
| 475 | |
| 476 if (DEBUG) Log.i(TAG, "useSharedRelros() called with " + bundle); | |
| 477 | |
| 478 synchronized (Linker.class) { | |
| 479 // Note that in certain cases, this can be called before | |
| 480 // initServiceProcess() in service processes. | |
| 481 sSharedRelros = bundle; | |
| 482 // Tell any listener blocked in finishLibraryLoad() about it. | |
| 483 Linker.class.notifyAll(); | |
| 484 } | |
| 485 } | |
| 486 | |
| 487 /** | |
| 488 * Call this to retrieve the shared RELRO sections created in this process, | |
| 489 * after loading all libraries. | |
| 490 * @return a new Bundle instance, or null if RELRO sharing is disabled on | |
| 491 * this system, or if initServiceProcess() was called previously. | |
| 492 */ | |
| 493 public static Bundle getSharedRelros() { | |
| 494 if (DEBUG) Log.i(TAG, "getSharedRelros() called"); | |
| 495 synchronized (Linker.class) { | |
| 496 if (!sInBrowserProcess) { | |
| 497 if (DEBUG) Log.i(TAG, "... returning null Bundle"); | |
| 498 return null; | |
| 499 } | |
| 500 | |
| 501 // Return the Bundle created in finishLibraryLoad(). | |
| 502 if (DEBUG) Log.i(TAG, "... returning " + sSharedRelros); | |
| 503 return sSharedRelros; | |
| 504 } | |
| 505 } | |
| 506 | |
| 507 | |
| 508 /** | |
| 509 * Call this method before loading any libraries to indicate that this | |
| 510 * process shall neither create or reuse shared RELRO sections. | |
| 511 */ | |
| 512 public static void disableSharedRelros() { | |
| 513 if (DEBUG) Log.i(TAG, "disableSharedRelros() called"); | |
| 514 synchronized (Linker.class) { | |
| 515 sInBrowserProcess = false; | |
| 516 sWaitForSharedRelros = false; | |
| 517 sBrowserUsesSharedRelro = false; | |
| 518 } | |
| 519 } | |
| 520 | |
| 521 /** | |
| 522 * Call this method before loading any libraries to indicate that this | |
| 523 * process is ready to reuse shared RELRO sections from another one. | |
| 524 * Typically used when starting service processes. | |
| 525 * @param baseLoadAddress the base library load address to use. | |
| 526 */ | |
| 527 public static void initServiceProcess(long baseLoadAddress) { | |
| 528 if (DEBUG) Log.i(TAG, String.format("initServiceProcess(0x%x) called", b
aseLoadAddress)); | |
| 529 synchronized (Linker.class) { | |
| 530 ensureInitializedLocked(); | |
| 531 sInBrowserProcess = false; | |
| 532 sBrowserUsesSharedRelro = false; | |
| 533 if (sRelroSharingSupported) { | |
| 534 sWaitForSharedRelros = true; | |
| 535 sBaseLoadAddress = baseLoadAddress; | |
| 536 sCurrentLoadAddress = baseLoadAddress; | |
| 537 } | |
| 538 } | |
| 539 } | |
| 540 | |
| 541 /** | |
| 542 * Retrieve the base load address of all shared RELRO sections. | |
| 543 * This also enforces the creation of shared RELRO sections in | |
| 544 * prepareLibraryLoad(), which can later be retrieved with getSharedRelros()
. | |
| 545 * @return a common, random base load address, or 0 if RELRO sharing is | |
| 546 * disabled. | |
| 547 */ | |
| 548 public static long getBaseLoadAddress() { | |
| 549 synchronized (Linker.class) { | |
| 550 ensureInitializedLocked(); | |
| 551 if (!sInBrowserProcess) { | |
| 552 Log.w(TAG, "Shared RELRO sections are disabled in this process!"
); | |
| 553 return 0; | |
| 554 } | |
| 555 | |
| 556 setupBaseLoadAddressLocked(); | |
| 557 if (DEBUG) Log.i(TAG, String.format("getBaseLoadAddress() returns 0x
%x", | |
| 558 sBaseLoadAddress)); | |
| 559 return sBaseLoadAddress; | |
| 560 } | |
| 561 } | |
| 562 | |
| 563 // Used internally to lazily setup the common random base load address. | |
| 564 private static void setupBaseLoadAddressLocked() { | |
| 565 assert Thread.holdsLock(Linker.class); | |
| 566 if (sBaseLoadAddress == 0) { | |
| 567 long address = computeRandomBaseLoadAddress(); | |
| 568 sBaseLoadAddress = address; | |
| 569 sCurrentLoadAddress = address; | |
| 570 if (address == 0) { | |
| 571 // If the computed address is 0, there are issues with the | |
| 572 // entropy source, so disable RELRO shared / fixed load addresse
s. | |
| 573 Log.w(TAG, "Disabling shared RELROs due to bad entropy sources")
; | |
| 574 sBrowserUsesSharedRelro = false; | |
| 575 sWaitForSharedRelros = false; | |
| 576 } | |
| 577 } | |
| 578 } | |
| 579 | |
| 580 | |
| 581 /** | |
| 582 * Compute a random base load address where to place loaded libraries. | |
| 583 * @return new base load address, or 0 if the system does not support | |
| 584 * RELRO sharing. | |
| 585 */ | |
| 586 private static long computeRandomBaseLoadAddress() { | |
| 587 // The kernel ASLR feature will place randomized mappings starting | |
| 588 // from this address. Never try to load anything above this | |
| 589 // explicitly to avoid random conflicts. | |
| 590 final long baseAddressLimit = 0x40000000; | |
| 591 | |
| 592 // Start loading libraries from this base address. | |
| 593 final long baseAddress = 0x20000000; | |
| 594 | |
| 595 // Maximum randomized base address value. Used to ensure a margin | |
| 596 // of 192 MB below baseAddressLimit. | |
| 597 final long baseAddressMax = baseAddressLimit - 192 * 1024 * 1024; | |
| 598 | |
| 599 // The maximum limit of the desired random offset. | |
| 600 final long pageSize = nativeGetPageSize(); | |
| 601 final int offsetLimit = (int) ((baseAddressMax - baseAddress) / pageSize
); | |
| 602 | |
| 603 // Get the greatest power of 2 that is smaller or equal to offsetLimit. | |
| 604 int numBits = 30; | |
| 605 for (; numBits > 1; numBits--) { | |
| 606 if ((1 << numBits) <= offsetLimit) | |
| 607 break; | |
| 608 } | |
| 609 | |
| 610 if (DEBUG) { | |
| 611 final int maxValue = (1 << numBits) - 1; | |
| 612 Log.i(TAG, String.format("offsetLimit=%d numBits=%d maxValue=%d (0x%
x)", | |
| 613 offsetLimit, numBits, maxValue, maxValue)); | |
| 614 } | |
| 615 | |
| 616 // Find a random offset between 0 and (2^numBits - 1), included. | |
| 617 int offset = getRandomBits(numBits); | |
| 618 long address = 0; | |
| 619 if (offset >= 0) | |
| 620 address = baseAddress + offset * pageSize; | |
| 621 | |
| 622 if (DEBUG) { | |
| 623 Log.i(TAG, | |
| 624 String.format("Linker.computeRandomBaseLoadAddress() return 0x
%x", | |
| 625 address)); | |
| 626 } | |
| 627 return address; | |
| 628 } | |
| 629 | |
| 630 /** | |
| 631 * Return a cryptographically-strong random number of numBits bits. | |
| 632 * @param numBits The number of bits in the result. Must be in 1..31 range. | |
| 633 * @return A random integer between 0 and (2^numBits - 1), inclusive, or -1 | |
| 634 * in case of error (e.g. if /dev/urandom can't be opened or read). | |
| 635 */ | |
| 636 private static int getRandomBits(int numBits) { | |
| 637 // Sanity check. | |
| 638 assert numBits > 0; | |
| 639 assert numBits < 32; | |
| 640 | |
| 641 FileInputStream input; | |
| 642 try { | |
| 643 // A naive implementation would read a 32-bit integer then use modul
o, but | |
| 644 // this introduces a slight bias. Instead, read 32-bit integers from
the | |
| 645 // entropy source until the value is positive but smaller than maxLi
mit. | |
| 646 input = new FileInputStream(new File("/dev/urandom")); | |
| 647 } catch (Exception e) { | |
| 648 Log.e(TAG, "Could not open /dev/urandom", e); | |
| 649 return -1; | |
| 650 } | |
| 651 | |
| 652 int result = 0; | |
| 653 try { | |
| 654 for (int n = 0; n < 4; n++) { | |
| 655 result = (result << 8) | (input.read() & 255); | |
| 656 } | |
| 657 } catch (Exception e) { | |
| 658 Log.e(TAG, "Could not read /dev/urandom", e); | |
| 659 return -1; | |
| 660 } finally { | |
| 661 try { | |
| 662 input.close(); | |
| 663 } catch (Exception e) { | |
| 664 // Can't really do anything here. | |
| 665 } | |
| 666 } | |
| 667 result &= (1 << numBits) - 1; | |
| 668 | |
| 669 if (DEBUG) { | |
| 670 Log.i(TAG, String.format( | |
| 671 "getRandomBits(%d) returned %d", numBits, result)); | |
| 672 } | |
| 673 | |
| 674 return result; | |
| 675 } | |
| 676 | |
| 677 // Used for debugging only. | |
| 678 private static void dumpBundle(Bundle bundle) { | |
| 679 if (DEBUG) Log.i(TAG, "Bundle has " + bundle.size() + " items: " + bundl
e); | |
| 680 } | |
| 681 | |
| 682 /** | |
| 683 * Use the shared RELRO section from a Bundle received form another process. | |
| 684 * Call this after calling setBaseLoadAddress() then loading all libraries | |
| 685 * with loadLibrary(). | |
| 686 * @param bundle Bundle instance generated with createSharedRelroBundle() in | |
| 687 * another process. | |
| 688 */ | |
| 689 private static void useSharedRelrosLocked(Bundle bundle) { | |
| 690 assert Thread.holdsLock(Linker.class); | |
| 691 | |
| 692 if (DEBUG) Log.i(TAG, "Linker.useSharedRelrosLocked() called"); | |
| 693 | |
| 694 if (bundle == null) { | |
| 695 if (DEBUG) Log.i(TAG, "null bundle!"); | |
| 696 return; | |
| 697 } | |
| 698 | |
| 699 if (!sRelroSharingSupported) { | |
| 700 if (DEBUG) Log.i(TAG, "System does not support RELRO sharing"); | |
| 701 return; | |
| 702 } | |
| 703 | |
| 704 if (sLoadedLibraries == null) { | |
| 705 if (DEBUG) Log.i(TAG, "No libraries loaded!"); | |
| 706 return; | |
| 707 } | |
| 708 | |
| 709 if (DEBUG) dumpBundle(bundle); | |
| 710 HashMap<String, LibInfo> relroMap = createLibInfoMapFromBundle(bundle); | |
| 711 | |
| 712 // Apply the RELRO section to all libraries that were already loaded. | |
| 713 for (Map.Entry<String, LibInfo> entry : relroMap.entrySet()) { | |
| 714 String libName = entry.getKey(); | |
| 715 LibInfo libInfo = entry.getValue(); | |
| 716 if (!nativeUseSharedRelro(libName, libInfo)) { | |
| 717 Log.w(TAG, "Could not use shared RELRO section for " + libName); | |
| 718 } else { | |
| 719 if (DEBUG) Log.i(TAG, "Using shared RELRO section for " + libNam
e); | |
| 720 } | |
| 721 } | |
| 722 | |
| 723 // In service processes, close all file descriptors from the map now. | |
| 724 if (!sInBrowserProcess) | |
| 725 closeLibInfoMap(relroMap); | |
| 726 | |
| 727 if (DEBUG) Log.i(TAG, "Linker.useSharedRelrosLocked() exiting"); | |
| 728 } | |
| 729 | |
| 730 /** | |
| 731 * Returns whether the linker was unable to load one library at a given fixe
d address. | |
| 732 * | |
| 733 * @return true if at least one library was not loaded at the expected fixed
address. | |
| 734 */ | |
| 735 public static boolean loadAtFixedAddressFailed() { | |
| 736 return sLoadAtFixedAddressFailed; | |
| 737 } | |
| 738 | |
| 739 /** | |
| 740 * Load a native shared library with the Chromium linker. | |
| 741 * If neither initSharedRelro() or readFromBundle() were called | |
| 742 * previously, this uses the standard linker (i.e. System.loadLibrary()). | |
| 743 * | |
| 744 * @param library The library's base name. | |
| 745 */ | |
| 746 public static void loadLibrary(String library) { | |
| 747 if (DEBUG) Log.i(TAG, "loadLibrary: " + library); | |
| 748 | |
| 749 // Don't self-load the linker. This is because the build system is | |
| 750 // not clever enough to understand that all the libraries packaged | |
| 751 // in the final .apk don't need to be explicitly loaded. | |
| 752 // Also deal with the component build that adds a .cr suffix to the name
. | |
| 753 if (library.equals(TAG) || library.equals(TAG + ".cr")) { | |
| 754 if (DEBUG) Log.i(TAG, "ignoring self-linker load"); | |
| 755 return; | |
| 756 } | |
| 757 | |
| 758 synchronized (Linker.class) { | |
| 759 ensureInitializedLocked(); | |
| 760 | |
| 761 // Security: Ensure prepareLibraryLoad() was called before. | |
| 762 // In theory, this can be done lazily here, but it's more consistent | |
| 763 // to use a pair of functions (i.e. prepareLibraryLoad() + finishLib
raryLoad()) | |
| 764 // that wrap all calls to loadLibrary() in the library loader. | |
| 765 assert sPrepareLibraryLoadCalled; | |
| 766 | |
| 767 String libName = System.mapLibraryName(library); | |
| 768 | |
| 769 if (sLoadedLibraries == null) | |
| 770 sLoadedLibraries = new HashMap<String, LibInfo>(); | |
| 771 | |
| 772 if (sLoadedLibraries.containsKey(libName)) { | |
| 773 if (DEBUG) Log.i(TAG, "Not loading " + libName + " twice"); | |
| 774 return; | |
| 775 } | |
| 776 | |
| 777 LibInfo libInfo = new LibInfo(); | |
| 778 long loadAddress = 0; | |
| 779 if ((sInBrowserProcess && sBrowserUsesSharedRelro) || sWaitForShared
Relros) { | |
| 780 // Load the library at a fixed address. | |
| 781 loadAddress = sCurrentLoadAddress; | |
| 782 } | |
| 783 | |
| 784 if (!nativeLoadLibrary(libName, loadAddress, libInfo)) { | |
| 785 String errorMessage = "Unable to load library: " + libName; | |
| 786 Log.e(TAG, errorMessage); | |
| 787 throw new UnsatisfiedLinkError(errorMessage); | |
| 788 } | |
| 789 // Keep track whether the library has been loaded at the expected lo
ad address. | |
| 790 if (loadAddress != 0 && loadAddress != libInfo.mLoadAddress) | |
| 791 sLoadAtFixedAddressFailed = true; | |
| 792 | |
| 793 // Print the load address to the logcat when testing the linker. The
format | |
| 794 // of the string is expected by the Python test_runner script as one
of: | |
| 795 // BROWSER_LIBRARY_ADDRESS: <library-name> <address> | |
| 796 // RENDERER_LIBRARY_ADDRESS: <library-name> <address> | |
| 797 // Where <library-name> is the library name, and <address> is the he
xadecimal load | |
| 798 // address. | |
| 799 if (NativeLibraries.ENABLE_LINKER_TESTS) { | |
| 800 Log.i(TAG, String.format( | |
| 801 "%s_LIBRARY_ADDRESS: %s %x", | |
| 802 sInBrowserProcess ? "BROWSER" : "RENDERER", | |
| 803 libName, | |
| 804 libInfo.mLoadAddress)); | |
| 805 } | |
| 806 | |
| 807 if (sInBrowserProcess) { | |
| 808 // Create a new shared RELRO section at the 'current' fixed load
address. | |
| 809 if (!nativeCreateSharedRelro(libName, sCurrentLoadAddress, libIn
fo)) { | |
| 810 Log.w(TAG, String.format("Could not create shared RELRO for
%s at %x", | |
| 811 libName, sCurrentLoadAddress)); | |
| 812 } else { | |
| 813 if (DEBUG) Log.i(TAG, | |
| 814 String.format( | |
| 815 "Created shared RELRO for %s at %x: %s", | |
| 816 libName, | |
| 817 sCurrentLoadAddress, | |
| 818 libInfo.toString())); | |
| 819 } | |
| 820 } | |
| 821 | |
| 822 if (sCurrentLoadAddress != 0) { | |
| 823 // Compute the next current load address. If sBaseLoadAddress | |
| 824 // is not 0, this is an explicit library load address. Otherwise
, | |
| 825 // this is an explicit load address for relocated RELRO sections | |
| 826 // only. | |
| 827 sCurrentLoadAddress = libInfo.mLoadAddress + libInfo.mLoadSize; | |
| 828 } | |
| 829 | |
| 830 sLoadedLibraries.put(libName, libInfo); | |
| 831 if (DEBUG) Log.i(TAG, "Library details " + libInfo.toString()); | |
| 832 } | |
| 833 } | |
| 834 | |
| 835 /** | |
| 836 * Native method used to load a library. | |
| 837 * @param library Platform specific library name (e.g. libfoo.so) | |
| 838 * @param loadAddress Explicit load address, or 0 for randomized one. | |
| 839 * @param libInfo If not null, the mLoadAddress and mLoadSize fields | |
| 840 * of this LibInfo instance will set on success. | |
| 841 * @return true for success, false otherwise. | |
| 842 */ | |
| 843 private static native boolean nativeLoadLibrary(String library, | |
| 844 long loadAddress, | |
| 845 LibInfo libInfo); | |
| 846 | |
| 847 /** | |
| 848 * Native method used to create a shared RELRO section. | |
| 849 * If the library was already loaded at the same address using | |
| 850 * nativeLoadLibrary(), this creates the RELRO for it. Otherwise, | |
| 851 * this loads a new temporary library at the specified address, | |
| 852 * creates and extracts the RELRO section from it, then unloads it. | |
| 853 * @param library Library name. | |
| 854 * @param loadAddress load address, which can be different from the one | |
| 855 * used to load the library in the current process! | |
| 856 * @param libInfo libInfo instance. On success, the mRelroStart, mRelroSize | |
| 857 * and mRelroFd will be set. | |
| 858 * @return true on success, false otherwise. | |
| 859 */ | |
| 860 private static native boolean nativeCreateSharedRelro(String library, | |
| 861 long loadAddress, | |
| 862 LibInfo libInfo); | |
| 863 | |
| 864 /** | |
| 865 * Native method used to use a shared RELRO section. | |
| 866 * @param library Library name. | |
| 867 * @param libInfo A LibInfo instance containing valid RELRO information | |
| 868 * @return true on success. | |
| 869 */ | |
| 870 private static native boolean nativeUseSharedRelro(String library, | |
| 871 LibInfo libInfo); | |
| 872 | |
| 873 /** | |
| 874 * Checks that the system supports shared RELROs. Old Android kernels | |
| 875 * have a bug in the way they check Ashmem region protection flags, which | |
| 876 * makes using shared RELROs unsafe. This method performs a simple runtime | |
| 877 * check for this misfeature, even though nativeEnableSharedRelro() will | |
| 878 * always fail if this returns false. | |
| 879 */ | |
| 880 private static native boolean nativeCanUseSharedRelro(); | |
| 881 | |
| 882 // Returns the native page size in bytes. | |
| 883 private static native long nativeGetPageSize(); | |
| 884 | |
| 885 /** | |
| 886 * Record information for a given library. | |
| 887 * IMPORTANT: Native code knows about this class's fields, so | |
| 888 * don't change them without modifying the corresponding C++ sources. | |
| 889 * Also, the LibInfo instance owns the ashmem file descriptor. | |
| 890 */ | |
| 891 public static class LibInfo implements Parcelable { | |
| 892 | |
| 893 public LibInfo() { | |
| 894 mLoadAddress = 0; | |
| 895 mLoadSize = 0; | |
| 896 mRelroStart = 0; | |
| 897 mRelroSize = 0; | |
| 898 mRelroFd = -1; | |
| 899 } | |
| 900 | |
| 901 public void close() { | |
| 902 if (mRelroFd >= 0) { | |
| 903 try { | |
| 904 ParcelFileDescriptor.adoptFd(mRelroFd).close(); | |
| 905 } catch (java.io.IOException e) { | |
| 906 } | |
| 907 mRelroFd = -1; | |
| 908 } | |
| 909 } | |
| 910 | |
| 911 // from Parcelable | |
| 912 public LibInfo(Parcel in) { | |
| 913 mLoadAddress = in.readLong(); | |
| 914 mLoadSize = in.readLong(); | |
| 915 mRelroStart = in.readLong(); | |
| 916 mRelroSize = in.readLong(); | |
| 917 ParcelFileDescriptor fd = in.readFileDescriptor(); | |
| 918 mRelroFd = fd.detachFd(); | |
| 919 } | |
| 920 | |
| 921 // from Parcelable | |
| 922 @Override | |
| 923 public void writeToParcel(Parcel out, int flags) { | |
| 924 if (mRelroFd >= 0) { | |
| 925 out.writeLong(mLoadAddress); | |
| 926 out.writeLong(mLoadSize); | |
| 927 out.writeLong(mRelroStart); | |
| 928 out.writeLong(mRelroSize); | |
| 929 try { | |
| 930 ParcelFileDescriptor fd = ParcelFileDescriptor.fromFd(mRelro
Fd); | |
| 931 fd.writeToParcel(out, 0); | |
| 932 fd.close(); | |
| 933 } catch (java.io.IOException e) { | |
| 934 Log.e(TAG, "Cant' write LibInfo file descriptor to parcel",
e); | |
| 935 } | |
| 936 } | |
| 937 } | |
| 938 | |
| 939 // from Parcelable | |
| 940 @Override | |
| 941 public int describeContents() { | |
| 942 return Parcelable.CONTENTS_FILE_DESCRIPTOR; | |
| 943 } | |
| 944 | |
| 945 // from Parcelable | |
| 946 public static final Parcelable.Creator<LibInfo> CREATOR = | |
| 947 new Parcelable.Creator<LibInfo>() { | |
| 948 @Override | |
| 949 public LibInfo createFromParcel(Parcel in) { | |
| 950 return new LibInfo(in); | |
| 951 } | |
| 952 | |
| 953 @Override | |
| 954 public LibInfo[] newArray(int size) { | |
| 955 return new LibInfo[size]; | |
| 956 } | |
| 957 }; | |
| 958 | |
| 959 @Override | |
| 960 public String toString() { | |
| 961 return String.format("[load=0x%x-0x%x relro=0x%x-0x%x fd=%d]", | |
| 962 mLoadAddress, | |
| 963 mLoadAddress + mLoadSize, | |
| 964 mRelroStart, | |
| 965 mRelroStart + mRelroSize, | |
| 966 mRelroFd); | |
| 967 } | |
| 968 | |
| 969 // IMPORTANT: Don't change these fields without modifying the | |
| 970 // native code that accesses them directly! | |
| 971 public long mLoadAddress; // page-aligned library load address. | |
| 972 public long mLoadSize; // page-aligned library load size. | |
| 973 public long mRelroStart; // page-aligned address in memory, or 0 if non
e. | |
| 974 public long mRelroSize; // page-aligned size in memory, or 0. | |
| 975 public int mRelroFd; // ashmem file descriptor, or -1 | |
| 976 } | |
| 977 | |
| 978 // Create a Bundle from a map of LibInfo objects. | |
| 979 private static Bundle createBundleFromLibInfoMap(HashMap<String, LibInfo> ma
p) { | |
| 980 Bundle bundle = new Bundle(map.size()); | |
| 981 for (Map.Entry<String, LibInfo> entry : map.entrySet()) { | |
| 982 bundle.putParcelable(entry.getKey(), entry.getValue()); | |
| 983 } | |
| 984 | |
| 985 return bundle; | |
| 986 } | |
| 987 | |
| 988 // Create a new LibInfo map from a Bundle. | |
| 989 private static HashMap<String, LibInfo> createLibInfoMapFromBundle(Bundle bu
ndle) { | |
| 990 HashMap<String, LibInfo> map = new HashMap<String, LibInfo>(); | |
| 991 for (String library : bundle.keySet()) { | |
| 992 LibInfo libInfo = bundle.getParcelable(library); | |
| 993 map.put(library, libInfo); | |
| 994 } | |
| 995 return map; | |
| 996 } | |
| 997 | |
| 998 // Call the close() method on all values of a LibInfo map. | |
| 999 private static void closeLibInfoMap(HashMap<String, LibInfo> map) { | |
| 1000 for (Map.Entry<String, LibInfo> entry : map.entrySet()) { | |
| 1001 entry.getValue().close(); | |
| 1002 } | |
| 1003 } | |
| 1004 | |
| 1005 // The map of libraries that are currently loaded in this process. | |
| 1006 private static HashMap<String, LibInfo> sLoadedLibraries = null; | |
| 1007 | |
| 1008 // Used to pass the shared RELRO Bundle through Binder. | |
| 1009 public static final String EXTRA_LINKER_SHARED_RELROS = | |
| 1010 "org.chromium.content.common.linker.shared_relros"; | |
| 1011 } | |
| OLD | NEW |