Adding allocation size check when creating arrays in java parcel

Unchecked allocations were causing OOM issues in services. Adding
a check while allocating array.

Bug: 205282403
Test: m && acloud delete --all && acloud create --local-image --local-instance
&& atest -c android.os.cts.ParcelTest

Change-Id: I8c728166540f22ebaded5025f7b3190568da5a6b
This commit is contained in:
Pawan Wagh
2023-01-18 23:33:27 +00:00
parent 923d93316e
commit b4c9d9ad1f

View File

@@ -362,6 +362,22 @@ public final class Parcel {
// see libbinder's binder/Status.h // see libbinder's binder/Status.h
private static final int EX_TRANSACTION_FAILED = -129; 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 @CriticalNative
private static native void nativeMarkSensitive(long nativePtr); private static native void nativeMarkSensitive(long nativePtr);
@FastNative @FastNative
@@ -1502,9 +1518,63 @@ public final class Parcel {
} }
} }
private static <T> int getItemTypeSize(@NonNull Class<T> 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 @Nullable
public final boolean[] createBooleanArray() { public final boolean[] createBooleanArray() {
int N = readInt(); 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 // >>2 as a fast divide-by-4 works in the create*Array() functions
// because dataAvail() will never return a negative number. 4 is // because dataAvail() will never return a negative number. 4 is
// the size of a stored boolean in the stream. // the size of a stored boolean in the stream.
@@ -1547,6 +1617,8 @@ public final class Parcel {
@Nullable @Nullable
public short[] createShortArray() { public short[] createShortArray() {
int n = readInt(); int n = readInt();
// Assuming size of 2 byte for short.
ensureWithinMemoryLimit(SIZE_SHORT, n);
if (n >= 0 && n <= (dataAvail() >> 2)) { if (n >= 0 && n <= (dataAvail() >> 2)) {
short[] val = new short[n]; short[] val = new short[n];
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
@@ -1585,6 +1657,8 @@ public final class Parcel {
@Nullable @Nullable
public final char[] createCharArray() { public final char[] createCharArray() {
int N = readInt(); int N = readInt();
// Assuming size of 2 byte for char.
ensureWithinMemoryLimit(SIZE_CHAR, N);
if (N >= 0 && N <= (dataAvail() >> 2)) { if (N >= 0 && N <= (dataAvail() >> 2)) {
char[] val = new char[N]; char[] val = new char[N];
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
@@ -1622,6 +1696,8 @@ public final class Parcel {
@Nullable @Nullable
public final int[] createIntArray() { public final int[] createIntArray() {
int N = readInt(); int N = readInt();
// Assuming size of 4 byte for int.
ensureWithinMemoryLimit(SIZE_INT, N);
if (N >= 0 && N <= (dataAvail() >> 2)) { if (N >= 0 && N <= (dataAvail() >> 2)) {
int[] val = new int[N]; int[] val = new int[N];
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
@@ -1659,6 +1735,8 @@ public final class Parcel {
@Nullable @Nullable
public final long[] createLongArray() { public final long[] createLongArray() {
int N = readInt(); int N = readInt();
// Assuming size of 8 byte for long.
ensureWithinMemoryLimit(SIZE_LONG, N);
// >>3 because stored longs are 64 bits // >>3 because stored longs are 64 bits
if (N >= 0 && N <= (dataAvail() >> 3)) { if (N >= 0 && N <= (dataAvail() >> 3)) {
long[] val = new long[N]; long[] val = new long[N];
@@ -1697,6 +1775,8 @@ public final class Parcel {
@Nullable @Nullable
public final float[] createFloatArray() { public final float[] createFloatArray() {
int N = readInt(); int N = readInt();
// Assuming size of 4 byte for float.
ensureWithinMemoryLimit(SIZE_FLOAT, N);
// >>2 because stored floats are 4 bytes // >>2 because stored floats are 4 bytes
if (N >= 0 && N <= (dataAvail() >> 2)) { if (N >= 0 && N <= (dataAvail() >> 2)) {
float[] val = new float[N]; float[] val = new float[N];
@@ -1735,6 +1815,8 @@ public final class Parcel {
@Nullable @Nullable
public final double[] createDoubleArray() { public final double[] createDoubleArray() {
int N = readInt(); int N = readInt();
// Assuming size of 8 byte for double.
ensureWithinMemoryLimit(SIZE_DOUBLE, N);
// >>3 because stored doubles are 8 bytes // >>3 because stored doubles are 8 bytes
if (N >= 0 && N <= (dataAvail() >> 3)) { if (N >= 0 && N <= (dataAvail() >> 3)) {
double[] val = new double[N]; double[] val = new double[N];
@@ -1788,6 +1870,7 @@ public final class Parcel {
@Nullable @Nullable
public final String[] createString8Array() { public final String[] createString8Array() {
int N = readInt(); int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) { if (N >= 0) {
String[] val = new String[N]; String[] val = new String[N];
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
@@ -1828,6 +1911,7 @@ public final class Parcel {
@Nullable @Nullable
public final String[] createString16Array() { public final String[] createString16Array() {
int N = readInt(); int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) { if (N >= 0) {
String[] val = new String[N]; String[] val = new String[N];
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
@@ -1920,6 +2004,7 @@ public final class Parcel {
@Nullable @Nullable
public final IBinder[] createBinderArray() { public final IBinder[] createBinderArray() {
int N = readInt(); int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) { if (N >= 0) {
IBinder[] val = new IBinder[N]; IBinder[] val = new IBinder[N];
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
@@ -1954,6 +2039,7 @@ public final class Parcel {
public final <T extends IInterface> T[] createInterfaceArray( public final <T extends IInterface> T[] createInterfaceArray(
@NonNull IntFunction<T[]> newArray, @NonNull Function<IBinder, T> asInterface) { @NonNull IntFunction<T[]> newArray, @NonNull Function<IBinder, T> asInterface) {
int N = readInt(); int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) { if (N >= 0) {
T[] val = newArray.apply(N); T[] val = newArray.apply(N);
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
@@ -3200,6 +3286,7 @@ public final class Parcel {
if (N < 0) { if (N < 0) {
return null; return null;
} }
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
FileDescriptor[] f = new FileDescriptor[N]; FileDescriptor[] f = new FileDescriptor[N];
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
f[i] = readRawFileDescriptor(); f[i] = readRawFileDescriptor();
@@ -3666,6 +3753,7 @@ public final class Parcel {
if (N < 0) { if (N < 0) {
return null; return null;
} }
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
ArrayList<T> l = new ArrayList<T>(N); ArrayList<T> l = new ArrayList<T>(N);
while (N > 0) { while (N > 0) {
l.add(readTypedObject(c)); l.add(readTypedObject(c));
@@ -3717,6 +3805,7 @@ public final class Parcel {
if (count < 0) { if (count < 0) {
return null; return null;
} }
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, count);
final SparseArray<T> array = new SparseArray<>(count); final SparseArray<T> array = new SparseArray<>(count);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
final int index = readInt(); final int index = readInt();
@@ -3745,6 +3834,7 @@ public final class Parcel {
if (count < 0) { if (count < 0) {
return null; return null;
} }
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, count);
final ArrayMap<String, T> map = new ArrayMap<>(count); final ArrayMap<String, T> map = new ArrayMap<>(count);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
final String key = readString(); final String key = readString();
@@ -3771,6 +3861,7 @@ public final class Parcel {
if (N < 0) { if (N < 0) {
return null; return null;
} }
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
ArrayList<String> l = new ArrayList<String>(N); ArrayList<String> l = new ArrayList<String>(N);
while (N > 0) { while (N > 0) {
l.add(readString()); l.add(readString());
@@ -3796,6 +3887,7 @@ public final class Parcel {
if (N < 0) { if (N < 0) {
return null; return null;
} }
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
ArrayList<IBinder> l = new ArrayList<IBinder>(N); ArrayList<IBinder> l = new ArrayList<IBinder>(N);
while (N > 0) { while (N > 0) {
l.add(readStrongBinder()); l.add(readStrongBinder());
@@ -3822,6 +3914,7 @@ public final class Parcel {
if (N < 0) { if (N < 0) {
return null; return null;
} }
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
ArrayList<T> l = new ArrayList<T>(N); ArrayList<T> l = new ArrayList<T>(N);
while (N > 0) { while (N > 0) {
l.add(asInterface.apply(readStrongBinder())); l.add(asInterface.apply(readStrongBinder()));
@@ -3981,6 +4074,7 @@ public final class Parcel {
if (N < 0) { if (N < 0) {
return null; return null;
} }
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
T[] l = c.newArray(N); T[] l = c.newArray(N);
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
l[i] = readTypedObject(c); l[i] = readTypedObject(c);
@@ -4209,6 +4303,10 @@ public final class Parcel {
while (innermost.isArray()) { while (innermost.isArray()) {
innermost = innermost.getComponentType(); innermost = innermost.getComponentType();
} }
int typeSize = getItemTypeSize(innermost);
ensureWithinMemoryLimit(typeSize, dimensions);
val = (T) Array.newInstance(innermost, dimensions); val = (T) Array.newInstance(innermost, dimensions);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
readFixedArray(Array.get(val, i)); readFixedArray(Array.get(val, i));
@@ -4265,6 +4363,10 @@ public final class Parcel {
while (innermost.isArray()) { while (innermost.isArray()) {
innermost = innermost.getComponentType(); innermost = innermost.getComponentType();
} }
int typeSize = getItemTypeSize(innermost);
ensureWithinMemoryLimit(typeSize, dimensions);
val = (T) Array.newInstance(innermost, dimensions); val = (T) Array.newInstance(innermost, dimensions);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
readFixedArray(Array.get(val, i), asInterface); readFixedArray(Array.get(val, i), asInterface);
@@ -4320,6 +4422,10 @@ public final class Parcel {
while (innermost.isArray()) { while (innermost.isArray()) {
innermost = innermost.getComponentType(); innermost = innermost.getComponentType();
} }
int typeSize = getItemTypeSize(innermost);
ensureWithinMemoryLimit(typeSize, dimensions);
val = (T) Array.newInstance(innermost, dimensions); val = (T) Array.newInstance(innermost, dimensions);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
readFixedArray(Array.get(val, i), c); readFixedArray(Array.get(val, i), c);
@@ -5065,6 +5171,7 @@ public final class Parcel {
if (n < 0) { if (n < 0) {
return null; return null;
} }
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
T[] p = (T[]) ((clazz == null) ? new Parcelable[n] : Array.newInstance(clazz, n)); T[] p = (T[]) ((clazz == null) ? new Parcelable[n] : Array.newInstance(clazz, n));
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
p[i] = readParcelableInternal(loader, clazz); p[i] = readParcelableInternal(loader, clazz);