Adding typed Parcel readMap and readHashMap APIs.

Added typed read API of `readMap` and `readHashMap`,
that takes extra clazz parameters check that the class written
on the wire is the same or a descendant from the one provided
as the key and value arguments.

Doing so could enhance the security of Parcel deserialization,
as it would prevent unexpected types of objects being deserialized.
More details can be found at go/safer-parcel.

Test: atest -d android.os.cts.ParcelTest
Bug: 195622897
Change-Id: Ife9d1d7277b6345a6e11856179c301339b2dc087
This commit is contained in:
Hao Ke
2021-11-05 19:48:58 +00:00
parent a1e47b652b
commit 446144c9a8
2 changed files with 61 additions and 11 deletions

View File

@@ -31465,6 +31465,7 @@ package android.os {
method public float readFloat();
method public void readFloatArray(@NonNull float[]);
method @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
method @Nullable public <K, V> java.util.HashMap<K,V> readHashMap(@Nullable ClassLoader, @NonNull Class<? extends K>, @NonNull Class<? extends V>);
method public int readInt();
method public void readIntArray(@NonNull int[]);
method public void readList(@NonNull java.util.List, @Nullable ClassLoader);
@@ -31472,6 +31473,7 @@ package android.os {
method public long readLong();
method public void readLongArray(@NonNull long[]);
method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);

View File

@@ -2896,8 +2896,24 @@ public final class Parcel {
* from the parcel at the current dataPosition().
*/
public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
int N = readInt();
readMapInternal(outVal, N, loader);
int n = readInt();
readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null);
}
/**
* Same as {@link #readMap(Map, ClassLoader)} but accepts {@code clazzKey} and
* {@code clazzValue} parameter as the types required for each key and value pair.
*
* @throws BadParcelableException If the item to be deserialized is not an instance of that
* class or any of its children class
*/
public <K, V> void readMap(@NonNull Map<? super K, ? super V> outVal,
@Nullable ClassLoader loader, @NonNull Class<K> clazzKey,
@NonNull Class<V> clazzValue) {
Objects.requireNonNull(clazzKey);
Objects.requireNonNull(clazzValue);
int n = readInt();
readMapInternal(outVal, n, loader, clazzKey, clazzValue);
}
/**
@@ -2935,15 +2951,37 @@ public final class Parcel {
@Nullable
public final HashMap readHashMap(@Nullable ClassLoader loader)
{
int N = readInt();
if (N < 0) {
int n = readInt();
if (n < 0) {
return null;
}
HashMap m = new HashMap(N);
readMapInternal(m, N, loader);
HashMap m = new HashMap(n);
readMapInternal(m, n, loader, /* clazzKey */ null, /* clazzValue */ null);
return m;
}
/**
* Same as {@link #readHashMap(ClassLoader)} but accepts {@code clazzKey} and
* {@code clazzValue} parameter as the types required for each key and value pair.
*
* @throws BadParcelableException if the item to be deserialized is not an instance of that
* class or any of its children class
*/
@SuppressLint({"ConcreteCollection", "NullableCollection"})
@Nullable
public <K, V> HashMap<K, V> readHashMap(@Nullable ClassLoader loader,
@NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
Objects.requireNonNull(clazzKey);
Objects.requireNonNull(clazzValue);
int n = readInt();
if (n < 0) {
return null;
}
HashMap<K, V> map = new HashMap<>(n);
readMapInternal(map, n, loader, clazzKey, clazzValue);
return map;
}
/**
* Read and return a new Bundle object from the parcel at the current
* dataPosition(). Returns null if the previously written Bundle object was
@@ -4328,13 +4366,23 @@ public final class Parcel {
destroy();
}
/* package */ void readMapInternal(@NonNull Map outVal, int N,
/**
* To be replaced by {@link #readMapInternal(Map, int, ClassLoader, Class, Class)}, but keep
* the old API for compatibility usages.
*/
/* package */ void readMapInternal(@NonNull Map outVal, int n,
@Nullable ClassLoader loader) {
while (N > 0) {
Object key = readValue(loader);
Object value = readValue(loader);
readMapInternal(outVal, n, loader, /* clazzKey */null, /* clazzValue */null);
}
/* package */ <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
@Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
@Nullable Class<V> clazzValue) {
while (n > 0) {
K key = readValue(loader, clazzKey);
V value = readValue(loader, clazzValue);
outVal.put(key, value);
N--;
n--;
}
}