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 |