diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 0f5fc34bcc210..8d07a861a8c22 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -363,6 +363,22 @@ public final class Parcel { // see libbinder's binder/Status.h private static final int EX_TRANSACTION_FAILED = -129; + // Allow limit of 1 MB for allocating arrays + private static final int ARRAY_ALLOCATION_LIMIT = 1000000; + + // Following type size are used to determine allocation size while creating arrays + private static final int SIZE_BYTE = 1; + private static final int SIZE_CHAR = 2; + private static final int SIZE_SHORT = 2; + private static final int SIZE_BOOLEAN = 4; + private static final int SIZE_INT = 4; + private static final int SIZE_FLOAT = 4; + private static final int SIZE_DOUBLE = 8; + private static final int SIZE_LONG = 8; + + // Assume the least possible size for complex objects + private static final int SIZE_COMPLEX_TYPE = 1; + @CriticalNative private static native void nativeMarkSensitive(long nativePtr); @FastNative @@ -1503,9 +1519,63 @@ public final class Parcel { } } + private static int getItemTypeSize(@NonNull Class arrayClass) { + final Class componentType = arrayClass.getComponentType(); + // typeSize has been referred from respective create*Array functions + if (componentType == boolean.class) { + return SIZE_BOOLEAN; + } else if (componentType == byte.class) { + return SIZE_BYTE; + } else if (componentType == char.class) { + return SIZE_CHAR; + } else if (componentType == int.class) { + return SIZE_INT; + } else if (componentType == long.class) { + return SIZE_LONG; + } else if (componentType == float.class) { + return SIZE_FLOAT; + } else if (componentType == double.class) { + return SIZE_DOUBLE; + } + + return SIZE_COMPLEX_TYPE; + } + + private void ensureWithinMemoryLimit(int typeSize, @NonNull int... dimensions) { + // For Multidimensional arrays, Calculate total object + // which will be allocated. + int totalObjects = 1; + try { + for (int dimension : dimensions) { + totalObjects = Math.multiplyExact(totalObjects, dimension); + } + } catch (ArithmeticException e) { + Log.e(TAG, "ArithmeticException occurred while multiplying dimensions " + e); + } + ensureWithinMemoryLimit(typeSize, totalObjects); + } + + private void ensureWithinMemoryLimit(int typeSize, @NonNull int length) { + int estimatedAllocationSize = 0; + try { + estimatedAllocationSize = Math.multiplyExact(typeSize, length); + } catch (ArithmeticException e) { + Log.e(TAG, "ArithmeticException occurred while multiplying values " + typeSize + + " and " + length + " Exception: " + e); + } + + boolean isInBinderTransaction = Binder.isDirectlyHandlingTransaction(); + if (isInBinderTransaction && (estimatedAllocationSize > ARRAY_ALLOCATION_LIMIT)) { + Log.e(TAG, "Trying to Allocate " + estimatedAllocationSize + + " memory, In Binder Transaction : " + isInBinderTransaction); + } + } + @Nullable public final boolean[] createBooleanArray() { int N = readInt(); + // Assuming size of 4 byte for boolean. + ensureWithinMemoryLimit(SIZE_BOOLEAN, N); // >>2 as a fast divide-by-4 works in the create*Array() functions // because dataAvail() will never return a negative number. 4 is // the size of a stored boolean in the stream. @@ -1548,6 +1618,8 @@ public final class Parcel { @Nullable public short[] createShortArray() { int n = readInt(); + // Assuming size of 2 byte for short. + ensureWithinMemoryLimit(SIZE_SHORT, n); if (n >= 0 && n <= (dataAvail() >> 2)) { short[] val = new short[n]; for (int i = 0; i < n; i++) { @@ -1586,6 +1658,8 @@ public final class Parcel { @Nullable public final char[] createCharArray() { int N = readInt(); + // Assuming size of 2 byte for char. + ensureWithinMemoryLimit(SIZE_CHAR, N); if (N >= 0 && N <= (dataAvail() >> 2)) { char[] val = new char[N]; for (int i=0; i= 0 && N <= (dataAvail() >> 2)) { int[] val = new int[N]; for (int i=0; i>3 because stored longs are 64 bits if (N >= 0 && N <= (dataAvail() >> 3)) { long[] val = new long[N]; @@ -1698,6 +1776,8 @@ public final class Parcel { @Nullable public final float[] createFloatArray() { int N = readInt(); + // Assuming size of 4 byte for float. + ensureWithinMemoryLimit(SIZE_FLOAT, N); // >>2 because stored floats are 4 bytes if (N >= 0 && N <= (dataAvail() >> 2)) { float[] val = new float[N]; @@ -1736,6 +1816,8 @@ public final class Parcel { @Nullable public final double[] createDoubleArray() { int N = readInt(); + // Assuming size of 8 byte for double. + ensureWithinMemoryLimit(SIZE_DOUBLE, N); // >>3 because stored doubles are 8 bytes if (N >= 0 && N <= (dataAvail() >> 3)) { double[] val = new double[N]; @@ -1789,6 +1871,7 @@ public final class Parcel { @Nullable public final String[] createString8Array() { int N = readInt(); + ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); if (N >= 0) { String[] val = new String[N]; for (int i=0; i= 0) { String[] val = new String[N]; for (int i=0; i= 0) { IBinder[] val = new IBinder[N]; for (int i=0; i T[] createInterfaceArray( @NonNull IntFunction newArray, @NonNull Function asInterface) { int N = readInt(); + ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); if (N >= 0) { T[] val = newArray.apply(N); for (int i=0; i l = new ArrayList(N); while (N > 0) { l.add(readTypedObject(c)); @@ -3721,6 +3809,7 @@ public final class Parcel { if (count < 0) { return null; } + ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, count); final SparseArray array = new SparseArray<>(count); for (int i = 0; i < count; i++) { final int index = readInt(); @@ -3749,6 +3838,7 @@ public final class Parcel { if (count < 0) { return null; } + ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, count); final ArrayMap map = new ArrayMap<>(count); for (int i = 0; i < count; i++) { final String key = readString(); @@ -3775,6 +3865,7 @@ public final class Parcel { if (N < 0) { return null; } + ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); ArrayList l = new ArrayList(N); while (N > 0) { l.add(readString()); @@ -3800,6 +3891,7 @@ public final class Parcel { if (N < 0) { return null; } + ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); ArrayList l = new ArrayList(N); while (N > 0) { l.add(readStrongBinder()); @@ -3826,6 +3918,7 @@ public final class Parcel { if (N < 0) { return null; } + ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); ArrayList l = new ArrayList(N); while (N > 0) { l.add(asInterface.apply(readStrongBinder())); @@ -3985,6 +4078,7 @@ public final class Parcel { if (N < 0) { return null; } + ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); T[] l = c.newArray(N); for (int i=0; i