Index: base/android/java/src/org/chromium/base/library_loader/Linker.java |
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 49a76b6a66d2ace3fede660143c564d0e49811a7..cf098ca1e60e97ed1b488ff215df0f6dcad64bcb 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 is 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 bytes = 192 * 1024 * 1024; |
rmcilroy
2014/08/14 13:19:07
nit - maxExpectedBytes
simonb (inactive)
2014/08/14 14:25:14
Done.
|
+ final long address = nativeGetRandomBaseLoadAddress(bytes); |
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); |
@@ -980,6 +912,17 @@ public class Linker { |
private static native long nativeGetPageSize(); |
rmcilroy
2014/08/14 13:19:07
I think you can also get rid of this, right?
simonb (inactive)
2014/08/14 14:25:14
Done. Good spot, thanks.
|
/** |
+ * Return an address that should successfully map. |
rmcilroy
2014/08/14 13:19:07
"Return a random address that should be free to be
simonb (inactive)
2014/08/14 14:25:14
Done.
|
+ * 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 bytes 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 bytes); |
rmcilroy
2014/08/14 13:19:07
nit - sizeBytes
simonb (inactive)
2014/08/14 14:25:14
Done.
|
+ |
+ /** |
* Record information for a given library. |
* IMPORTANT: Native code knows about this class's fields, so |
* don't change them without modifying the corresponding C++ sources. |