From bcc7865ed6be3900ff7184a560148a6ff9b5f7ca Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Mon, 19 Dec 2022 17:24:31 -0500 Subject: [PATCH] Limit the size of lists in ZenPolicy These lists are already size-limited by the set of things they may depict, but this change enforces the list sizes upon reading from a parcel in case any callers change the list sizes via reflection or something like it. Bug: 251427644 Test: atest ZenPolicyTest Change-Id: I301ee58afa03b6695294e8a46280169ecbbd3ba1 --- .../service/notification/ZenPolicy.java | 33 ++++++++-- .../server/notification/ZenPolicyTest.java | 65 +++++++++++++++++++ 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java index a892570f589c1..bffa660cdbd97 100644 --- a/core/java/android/service/notification/ZenPolicy.java +++ b/core/java/android/service/notification/ZenPolicy.java @@ -79,6 +79,12 @@ public final class ZenPolicy implements Parcelable { /** @hide */ public static final int PRIORITY_CATEGORY_CONVERSATIONS = 8; + /** + * Total number of priority categories. Keep updated with any updates to PriorityCategory enum. + * @hide + */ + public static final int NUM_PRIORITY_CATEGORIES = 9; + /** @hide */ @IntDef(prefix = { "VISUAL_EFFECT_" }, value = { VISUAL_EFFECT_FULL_SCREEN_INTENT, @@ -107,6 +113,12 @@ public final class ZenPolicy implements Parcelable { /** @hide */ public static final int VISUAL_EFFECT_NOTIFICATION_LIST = 6; + /** + * Total number of visual effects. Keep updated with any updates to VisualEffect enum. + * @hide + */ + public static final int NUM_VISUAL_EFFECTS = 7; + /** @hide */ @IntDef(prefix = { "PEOPLE_TYPE_" }, value = { PEOPLE_TYPE_UNSET, @@ -202,8 +214,8 @@ public final class ZenPolicy implements Parcelable { /** @hide */ public ZenPolicy() { - mPriorityCategories = new ArrayList<>(Collections.nCopies(9, 0)); - mVisualEffects = new ArrayList<>(Collections.nCopies(7, 0)); + mPriorityCategories = new ArrayList<>(Collections.nCopies(NUM_PRIORITY_CATEGORIES, 0)); + mVisualEffects = new ArrayList<>(Collections.nCopies(NUM_VISUAL_EFFECTS, 0)); } /** @@ -804,8 +816,12 @@ public final class ZenPolicy implements Parcelable { @Override public ZenPolicy createFromParcel(Parcel source) { ZenPolicy policy = new ZenPolicy(); - policy.mPriorityCategories = source.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class); - policy.mVisualEffects = source.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class); + policy.mPriorityCategories = trimList( + source.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class), + NUM_PRIORITY_CATEGORIES); + policy.mVisualEffects = trimList( + source.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class), + NUM_VISUAL_EFFECTS); policy.mPriorityCalls = source.readInt(); policy.mPriorityMessages = source.readInt(); policy.mConversationSenders = source.readInt(); @@ -832,6 +848,15 @@ public final class ZenPolicy implements Parcelable { .toString(); } + // Returns a list containing the first maxLength elements of the input list if the list is + // longer than that size. For the lists in ZenPolicy, this should not happen unless the input + // is corrupt. + private static ArrayList trimList(ArrayList list, int maxLength) { + if (list == null || list.size() <= maxLength) { + return list; + } + return new ArrayList<>(list.subList(0, maxLength)); + } private String priorityCategoriesToString() { StringBuilder builder = new StringBuilder(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java index b16ca8b92848c..b4a294d09b7eb 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java @@ -19,6 +19,7 @@ package com.android.server.notification; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.fail; +import android.os.Parcel; import android.service.notification.ZenPolicy; import android.service.notification.nano.DNDPolicyProto; import android.test.suitebuilder.annotation.SmallTest; @@ -32,9 +33,13 @@ import com.google.protobuf.nano.InvalidProtocolBufferNanoException; import org.junit.Test; import org.junit.runner.RunWith; +import java.lang.reflect.Field; +import java.util.ArrayList; + @SmallTest @RunWith(AndroidJUnit4.class) public class ZenPolicyTest extends UiServiceTestCase { + private static final String CLASS = "android.service.notification.ZenPolicy"; @Test public void testZenPolicyApplyAllowedToDisallowed() { @@ -524,6 +529,66 @@ public class ZenPolicyTest extends UiServiceTestCase { assertProtoMatches(policy, policy.toProto()); } + @Test + public void testTooLongLists_fromParcel() { + ArrayList longList = new ArrayList(50); + for (int i = 0; i < 50; i++) { + longList.add(ZenPolicy.STATE_UNSET); + } + + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + ZenPolicy policy = builder.build(); + + try { + Field priorityCategories = Class.forName(CLASS).getDeclaredField( + "mPriorityCategories"); + priorityCategories.setAccessible(true); + priorityCategories.set(policy, longList); + + Field visualEffects = Class.forName(CLASS).getDeclaredField("mVisualEffects"); + visualEffects.setAccessible(true); + visualEffects.set(policy, longList); + } catch (NoSuchFieldException e) { + fail(e.toString()); + } catch (ClassNotFoundException e) { + fail(e.toString()); + } catch (IllegalAccessException e) { + fail(e.toString()); + } + + Parcel parcel = Parcel.obtain(); + policy.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + ZenPolicy fromParcel = ZenPolicy.CREATOR.createFromParcel(parcel); + + // Confirm that all the fields are accessible and UNSET + assertAllPriorityCategoriesUnsetExcept(fromParcel, -1); + assertAllVisualEffectsUnsetExcept(fromParcel, -1); + + // Because we don't access the lists directly, we also need to use reflection to make sure + // the lists are the right length. + try { + Field priorityCategories = Class.forName(CLASS).getDeclaredField( + "mPriorityCategories"); + priorityCategories.setAccessible(true); + ArrayList pcList = (ArrayList) priorityCategories.get(fromParcel); + assertEquals(ZenPolicy.NUM_PRIORITY_CATEGORIES, pcList.size()); + + + Field visualEffects = Class.forName(CLASS).getDeclaredField("mVisualEffects"); + visualEffects.setAccessible(true); + ArrayList veList = (ArrayList) visualEffects.get(fromParcel); + assertEquals(ZenPolicy.NUM_VISUAL_EFFECTS, veList.size()); + } catch (NoSuchFieldException e) { + fail(e.toString()); + } catch (ClassNotFoundException e) { + fail(e.toString()); + } catch (IllegalAccessException e) { + fail(e.toString()); + } + } + private void assertAllPriorityCategoriesUnsetExcept(ZenPolicy policy, int except) { if (except != ZenPolicy.PRIORITY_CATEGORY_REMINDERS) { assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryReminders());