summaryrefslogtreecommitdiffstats
path: root/base/android/java
diff options
context:
space:
mode:
authorsimonb@chromium.org <simonb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-21 17:06:39 +0000
committersimonb@chromium.org <simonb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-21 17:08:10 +0000
commit1bb001059e2e1420ef095b0fd15b9d9199cf366e (patch)
treecc644fadcba9da7dcc0ed75bfc2be9b55a2ed619 /base/android/java
parent4c07a715534ad3639ccde83a5ecd7b6780bd4e69 (diff)
downloadchromium_src-1bb001059e2e1420ef095b0fd15b9d9199cf366e.zip
chromium_src-1bb001059e2e1420ef095b0fd15b9d9199cf366e.tar.gz
chromium_src-1bb001059e2e1420ef095b0fd15b9d9199cf366e.tar.bz2
Switch from local random address generation to kernel ASLR
The current random base address generation in the Android chromium linker is prone to error. It selects an address at random between 0x20000000 and 0x40000000 and expects that this will be clear. This is occasionally untrue for ARM, but very often untrue for MIPS. As a consequence, RELRO sharing is being turned off more frequently than it could be. This change removes the local random address generation code and instead replaces it with code that speculatively maps a large region, captures the address returned by mmap, then unmaps and returns the address. The expectation is that this region will remain free for use when the time comes for the crazy linker to map the browser into it. This generally holds because the time between these two actions is short and little, if anything, loads or mmaps between them. Worst case is that RELRO sharing turns off as at present, but the probability of this happening should now be much lower. Note that capturing the address from mmap relies on Android ASLR being active for mmap. This is the default device state since ICS. The revised random browser load address is only as entropic as Android's ASLR. BUG=397634 Review URL: https://codereview.chromium.org/470053003 Cr-Commit-Position: refs/heads/master@{#291111} git-svn-id: svn://svn.chromium.org/chrome/trunk/src@291111 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/android/java')
-rw-r--r--base/android/java/src/org/chromium/base/library_loader/Linker.java124
1 files changed, 32 insertions, 92 deletions
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
index 49a76b6..15968ad 100644
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -13,8 +13,6 @@ import android.util.Log;
import org.chromium.base.SysUtils;
import org.chromium.base.ThreadUtils;
-import java.io.File;
-import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@@ -595,9 +593,9 @@ public class Linker {
sBaseLoadAddress = address;
sCurrentLoadAddress = address;
if (address == 0) {
- // If the computed address is 0, there are issues with the
- // entropy source, so disable RELRO shared / fixed load addresses.
- Log.w(TAG, "Disabling shared RELROs due to bad entropy sources");
+ // If the computed address is 0, there are issues with finding enough
+ // free address space, so disable RELRO shared / fixed load addresses.
+ Log.w(TAG, "Disabling shared RELROs due address space pressure");
sBrowserUsesSharedRelro = false;
sWaitForSharedRelros = false;
}
@@ -606,101 +604,35 @@ public class Linker {
/**
- * Compute a random base load address where to place loaded libraries.
+ * Compute a random base load address at which to place loaded libraries.
* @return new base load address, or 0 if the system does not support
* RELRO sharing.
*/
private static long computeRandomBaseLoadAddress() {
- // The kernel ASLR feature will place randomized mappings starting
- // from this address. Never try to load anything above this
- // explicitly to avoid random conflicts.
- final long baseAddressLimit = 0x40000000;
-
- // Start loading libraries from this base address.
- final long baseAddress = 0x20000000;
-
- // Maximum randomized base address value. Used to ensure a margin
- // of 192 MB below baseAddressLimit.
- final long baseAddressMax = baseAddressLimit - 192 * 1024 * 1024;
-
- // The maximum limit of the desired random offset.
- final long pageSize = nativeGetPageSize();
- final int offsetLimit = (int) ((baseAddressMax - baseAddress) / pageSize);
-
- // Get the greatest power of 2 that is smaller or equal to offsetLimit.
- int numBits = 30;
- for (; numBits > 1; numBits--) {
- if ((1 << numBits) <= offsetLimit)
- break;
- }
-
- if (DEBUG) {
- final int maxValue = (1 << numBits) - 1;
- Log.i(TAG, String.format(Locale.US, "offsetLimit=%d numBits=%d maxValue=%d (0x%x)",
- offsetLimit, numBits, maxValue, maxValue));
- }
-
- // Find a random offset between 0 and (2^numBits - 1), included.
- int offset = getRandomBits(numBits);
- long address = 0;
- if (offset >= 0)
- address = baseAddress + offset * pageSize;
-
+ // nativeGetRandomBaseLoadAddress() returns an address at which it has previously
+ // successfully mapped an area of the given size, on the basis that we will be
+ // able, with high probability, to map our library into it.
+ //
+ // One issue with this is that we do not yet know the size of the library that
+ // we will load is. So here we pass a value that we expect will always be larger
+ // than that needed. If it is smaller the library mapping may still succeed. The
+ // other issue is that although highly unlikely, there is no guarantee that
+ // something else does not map into the area we are going to use between here and
+ // when we try to map into it.
+ //
+ // The above notes mean that all of this is probablistic. It is however okay to do
+ // because if, worst case and unlikely, we get unlucky in our choice of address,
+ // the back-out and retry without the shared RELRO in the ChildProcessService will
+ // keep things running.
+ final long maxExpectedBytes = 192 * 1024 * 1024;
+ final long address = nativeGetRandomBaseLoadAddress(maxExpectedBytes);
if (DEBUG) {
Log.i(TAG,
- String.format(Locale.US, "Linker.computeRandomBaseLoadAddress() return 0x%x",
- address));
+ String.format(Locale.US, "Random native base load address: 0x%x", address));
}
return address;
}
- /**
- * Return a cryptographically-strong random number of numBits bits.
- * @param numBits The number of bits in the result. Must be in 1..31 range.
- * @return A random integer between 0 and (2^numBits - 1), inclusive, or -1
- * in case of error (e.g. if /dev/urandom can't be opened or read).
- */
- private static int getRandomBits(int numBits) {
- // Sanity check.
- assert numBits > 0;
- assert numBits < 32;
-
- FileInputStream input;
- try {
- // A naive implementation would read a 32-bit integer then use modulo, but
- // this introduces a slight bias. Instead, read 32-bit integers from the
- // entropy source until the value is positive but smaller than maxLimit.
- input = new FileInputStream(new File("/dev/urandom"));
- } catch (Exception e) {
- Log.e(TAG, "Could not open /dev/urandom", e);
- return -1;
- }
-
- int result = 0;
- try {
- for (int n = 0; n < 4; n++) {
- result = (result << 8) | (input.read() & 255);
- }
- } catch (Exception e) {
- Log.e(TAG, "Could not read /dev/urandom", e);
- return -1;
- } finally {
- try {
- input.close();
- } catch (Exception e) {
- // Can't really do anything here.
- }
- }
- result &= (1 << numBits) - 1;
-
- if (DEBUG) {
- Log.i(TAG, String.format(
- Locale.US, "getRandomBits(%d) returned %d", numBits, result));
- }
-
- return result;
- }
-
// Used for debugging only.
private static void dumpBundle(Bundle bundle) {
if (DEBUG) Log.i(TAG, "Bundle has " + bundle.size() + " items: " + bundle);
@@ -976,8 +908,16 @@ public class Linker {
*/
private static native boolean nativeCanUseSharedRelro();
- // Returns the native page size in bytes.
- private static native long nativeGetPageSize();
+ /**
+ * Return a random address that should be free to be mapped with the given size.
+ * Maps an area of size bytes, and if successful then unmaps it and returns
+ * the address of the area allocated by the system (with ASLR). The idea is
+ * that this area should remain free of other mappings until we map our library
+ * into it.
+ * @param sizeBytes Size of area in bytes to search for.
+ * @return address to pass to future mmap, or 0 on error.
+ */
+ private static native long nativeGetRandomBaseLoadAddress(long sizeBytes);
/**
* Record information for a given library.