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