Home | History | Annotate | Download | only in pm
      1 package android.content.pm;
      2 
      3 import android.os.Parcel;
      4 import android.os.Parcelable;
      5 import android.support.test.filters.LargeTest;
      6 
      7 import junit.framework.TestCase;
      8 
      9 import java.util.ArrayList;
     10 import java.util.Collections;
     11 import java.util.List;
     12 
     13 @LargeTest
     14 public class ParceledListSliceTest extends TestCase {
     15 
     16     public void testSmallList() throws Exception {
     17         final int objectCount = 100;
     18         List<SmallObject> list = new ArrayList<SmallObject>();
     19         for (int i = 0; i < objectCount; i++) {
     20             list.add(new SmallObject(i * 2, (i * 2) + 1));
     21         }
     22 
     23         ParceledListSlice<SmallObject> slice;
     24 
     25         Parcel parcel = Parcel.obtain();
     26         try {
     27             parcel.writeParcelable(new ParceledListSlice<SmallObject>(list), 0);
     28             parcel.setDataPosition(0);
     29             slice = parcel.readParcelable(getClass().getClassLoader());
     30         } finally {
     31             parcel.recycle();
     32         }
     33 
     34         assertNotNull(slice);
     35         assertNotNull(slice.getList());
     36         assertEquals(objectCount, slice.getList().size());
     37 
     38         for (int i = 0; i < objectCount; i++) {
     39             assertEquals(i * 2, slice.getList().get(i).mFieldA);
     40             assertEquals((i * 2) + 1, slice.getList().get(i).mFieldB);
     41         }
     42     }
     43 
     44     private static int measureLargeObject() {
     45         Parcel p = Parcel.obtain();
     46         try {
     47             new LargeObject(0, 0, 0, 0, 0).writeToParcel(p, 0);
     48             return p.dataPosition();
     49         } finally {
     50             p.recycle();
     51         }
     52     }
     53 
     54     /**
     55      * Test that when the list is large, the data is successfully parceled
     56      * and unparceled (the implementation will send pieces of the list in
     57      * separate round-trips to avoid the IPC limit).
     58      */
     59     public void testLargeList() throws Exception {
     60         final int thresholdBytes = 256 * 1024;
     61         final int objectCount = thresholdBytes / measureLargeObject();
     62 
     63         List<LargeObject> list = new ArrayList<LargeObject>();
     64         for (int i = 0; i < objectCount; i++) {
     65             list.add(new LargeObject(
     66                     i * 5,
     67                     (i * 5) + 1,
     68                     (i * 5) + 2,
     69                     (i * 5) + 3,
     70                     (i * 5) + 4
     71             ));
     72         }
     73 
     74         ParceledListSlice<LargeObject> slice;
     75 
     76         Parcel parcel = Parcel.obtain();
     77         try {
     78             parcel.writeParcelable(new ParceledListSlice<LargeObject>(list), 0);
     79             parcel.setDataPosition(0);
     80             slice = parcel.readParcelable(getClass().getClassLoader());
     81         } finally {
     82             parcel.recycle();
     83         }
     84 
     85         assertNotNull(slice);
     86         assertNotNull(slice.getList());
     87         assertEquals(objectCount, slice.getList().size());
     88 
     89         for (int i = 0; i < objectCount; i++) {
     90             assertEquals(i * 5, slice.getList().get(i).mFieldA);
     91             assertEquals((i * 5) + 1, slice.getList().get(i).mFieldB);
     92             assertEquals((i * 5) + 2, slice.getList().get(i).mFieldC);
     93             assertEquals((i * 5) + 3, slice.getList().get(i).mFieldD);
     94             assertEquals((i * 5) + 4, slice.getList().get(i).mFieldE);
     95         }
     96     }
     97 
     98     private void sendParcelStringList(List<String> list) {
     99         StringParceledListSlice slice;
    100         Parcel parcel = Parcel.obtain();
    101 
    102         try {
    103             parcel.writeParcelable(new StringParceledListSlice(list), 0);
    104             parcel.setDataPosition(0);
    105             slice = parcel.readParcelable(getClass().getClassLoader());
    106         } finally {
    107             parcel.recycle();
    108         }
    109 
    110         assertNotNull(slice);
    111         assertNotNull(slice.getList());
    112         assertEquals(list, slice.getList());
    113     }
    114 
    115     public void testStringList() throws Exception {
    116         final int objectCount = 400;
    117         List<String> list = new ArrayList<String>();
    118         for (long i = 0; i < objectCount; i++) {
    119             list.add(Long.toString(i * (6 - i)));
    120         }
    121 
    122         sendParcelStringList(list);
    123     }
    124 
    125     public void testLargeStringList() throws Exception {
    126         final int thresholdBytes = 256 * 1024;
    127         final String value = Long.toString(Long.MAX_VALUE);
    128         final int objectCount = 2 * thresholdBytes / value.length();
    129         final List<String> list = Collections.nCopies(objectCount, value);
    130 
    131         sendParcelStringList(list);
    132     }
    133 
    134 
    135     /**
    136      * Test that only homogeneous elements may be unparceled.
    137      */
    138     public void testHomogeneousElements() throws Exception {
    139         List<BaseObject> list = new ArrayList<BaseObject>();
    140         list.add(new LargeObject(0, 1, 2, 3, 4));
    141         list.add(new SmallObject(5, 6));
    142         list.add(new SmallObject(7, 8));
    143 
    144         Parcel parcel = Parcel.obtain();
    145         try {
    146             writeEvilParceledListSlice(parcel, list);
    147             parcel.setDataPosition(0);
    148             try {
    149                 ParceledListSlice.CREATOR.createFromParcel(parcel, getClass().getClassLoader());
    150                 assertTrue("Unparceled heterogeneous ParceledListSlice", false);
    151             } catch (IllegalArgumentException e) {
    152                 // Success, we're not allowed to process heterogeneous
    153                 // elements in a ParceledListSlice.
    154             }
    155         } finally {
    156             parcel.recycle();
    157         }
    158     }
    159 
    160     /**
    161      * Write a ParcelableListSlice that uses the BaseObject base class as the Creator.
    162      * This is dangerous, as it may affect how the data is unparceled, then later parceled
    163      * by the system, leading to a self-modifying data security vulnerability.
    164      */
    165     private static <T extends BaseObject> void writeEvilParceledListSlice(Parcel dest, List<T> list) {
    166         final int listCount = list.size();
    167 
    168         // Number of items.
    169         dest.writeInt(listCount);
    170 
    171         // The type/creator to use when unparceling. Here we use the base class
    172         // to simulate an attack on ParceledListSlice.
    173         dest.writeString(BaseObject.class.getName());
    174 
    175         for (int i = 0; i < listCount; i++) {
    176             // 1 means the item is present.
    177             dest.writeInt(1);
    178             list.get(i).writeToParcel(dest, 0);
    179         }
    180     }
    181 
    182     public abstract static class BaseObject implements Parcelable {
    183         protected static final int TYPE_SMALL = 0;
    184         protected static final int TYPE_LARGE = 1;
    185 
    186         protected void writeToParcel(Parcel dest, int flags, int type) {
    187             dest.writeInt(type);
    188         }
    189 
    190         @Override
    191         public int describeContents() {
    192             return 0;
    193         }
    194 
    195         /**
    196          * This is *REALLY* bad, but we're doing it in the test to ensure that we handle
    197          * the possible exploit when unparceling an object with the BaseObject written as
    198          * Creator.
    199          */
    200         public static final Creator<BaseObject> CREATOR = new Creator<BaseObject>() {
    201             @Override
    202             public BaseObject createFromParcel(Parcel source) {
    203                 switch (source.readInt()) {
    204                     case TYPE_SMALL:
    205                         return SmallObject.createFromParcelBody(source);
    206                     case TYPE_LARGE:
    207                         return LargeObject.createFromParcelBody(source);
    208                     default:
    209                         throw new IllegalArgumentException("Unknown type");
    210                 }
    211             }
    212 
    213             @Override
    214             public BaseObject[] newArray(int size) {
    215                 return new BaseObject[size];
    216             }
    217         };
    218     }
    219 
    220     public static class SmallObject extends BaseObject {
    221         public int mFieldA;
    222         public int mFieldB;
    223 
    224         public SmallObject(int a, int b) {
    225             mFieldA = a;
    226             mFieldB = b;
    227         }
    228 
    229         @Override
    230         public void writeToParcel(Parcel dest, int flags) {
    231             super.writeToParcel(dest, flags, TYPE_SMALL);
    232             dest.writeInt(mFieldA);
    233             dest.writeInt(mFieldB);
    234         }
    235 
    236         public static SmallObject createFromParcelBody(Parcel source) {
    237             return new SmallObject(source.readInt(), source.readInt());
    238         }
    239 
    240         public static final Creator<SmallObject> CREATOR = new Creator<SmallObject>() {
    241             @Override
    242             public SmallObject createFromParcel(Parcel source) {
    243                 // Consume the type (as it is always written out).
    244                 source.readInt();
    245                 return createFromParcelBody(source);
    246             }
    247 
    248             @Override
    249             public SmallObject[] newArray(int size) {
    250                 return new SmallObject[size];
    251             }
    252         };
    253     }
    254 
    255     public static class LargeObject extends BaseObject {
    256         public int mFieldA;
    257         public int mFieldB;
    258         public int mFieldC;
    259         public int mFieldD;
    260         public int mFieldE;
    261 
    262         public LargeObject(int a, int b, int c, int d, int e) {
    263             mFieldA = a;
    264             mFieldB = b;
    265             mFieldC = c;
    266             mFieldD = d;
    267             mFieldE = e;
    268         }
    269 
    270         @Override
    271         public void writeToParcel(Parcel dest, int flags) {
    272             super.writeToParcel(dest, flags, TYPE_LARGE);
    273             dest.writeInt(mFieldA);
    274             dest.writeInt(mFieldB);
    275             dest.writeInt(mFieldC);
    276             dest.writeInt(mFieldD);
    277             dest.writeInt(mFieldE);
    278         }
    279 
    280         public static LargeObject createFromParcelBody(Parcel source) {
    281             return new LargeObject(
    282                     source.readInt(),
    283                     source.readInt(),
    284                     source.readInt(),
    285                     source.readInt(),
    286                     source.readInt()
    287             );
    288         }
    289 
    290         public static final Creator<LargeObject> CREATOR = new Creator<LargeObject>() {
    291             @Override
    292             public LargeObject createFromParcel(Parcel source) {
    293                 // Consume the type (as it is always written out).
    294                 source.readInt();
    295                 return createFromParcelBody(source);
    296             }
    297 
    298             @Override
    299             public LargeObject[] newArray(int size) {
    300                 return new LargeObject[size];
    301             }
    302         };
    303     }
    304 }
    305