From 446144c9a8cb5b7c167e95420a7d2ad9741d72c0 Mon Sep 17 00:00:00 2001 From: Hao Ke Date: Fri, 5 Nov 2021 19:48:58 +0000 Subject: 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 --- core/api/current.txt | 2 ++ core/java/android/os/Parcel.java | 70 +++++++++++++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 9d1a171088eb..e94eb1b48fc3 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -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 java.util.HashMap readHashMap(@Nullable ClassLoader, @NonNull Class, @NonNull Class); 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 void readMap(@NonNull java.util.Map, @Nullable ClassLoader, @NonNull Class, @NonNull Class); method @Nullable public T readParcelable(@Nullable ClassLoader); method @Nullable public T readParcelable(@Nullable ClassLoader, @NonNull Class); method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader); diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index d1e671691897..fa3903468d9c 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -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 void readMap(@NonNull Map outVal, + @Nullable ClassLoader loader, @NonNull Class clazzKey, + @NonNull Class 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 HashMap readHashMap(@Nullable ClassLoader loader, + @NonNull Class clazzKey, @NonNull Class clazzValue) { + Objects.requireNonNull(clazzKey); + Objects.requireNonNull(clazzValue); + int n = readInt(); + if (n < 0) { + return null; + } + HashMap 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 */ void readMapInternal(@NonNull Map outVal, int n, + @Nullable ClassLoader loader, @Nullable Class clazzKey, + @Nullable Class clazzValue) { + while (n > 0) { + K key = readValue(loader, clazzKey); + V value = readValue(loader, clazzValue); outVal.put(key, value); - N--; + n--; } } -- cgit v1.2.3-59-g8ed1b