1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 package android.appsecurity.cts; 17 18 import static org.junit.Assert.assertFalse; 19 import static org.junit.Assert.assertNotEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.fail; 22 23 import android.platform.test.annotations.AppModeFull; 24 import com.android.ddmlib.Log.LogLevel; 25 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 26 import com.android.tradefed.build.IBuildInfo; 27 import com.android.tradefed.device.DeviceNotAvailableException; 28 import com.android.tradefed.device.ITestDevice; 29 import com.android.tradefed.log.LogUtil.CLog; 30 import com.android.tradefed.testtype.DeviceTestCase; 31 import com.android.tradefed.testtype.IBuildReceiver; 32 import com.android.tradefed.util.FileUtil; 33 34 import org.junit.After; 35 import org.junit.Assert; 36 import org.junit.Before; 37 38 import java.io.File; 39 import java.io.FileNotFoundException; 40 41 /** 42 * Set of tests that verify that corrupt APKs are properly rejected by PackageManager and 43 * do not cause the system to crash. 44 */ 45 @AppModeFull(reason = "the corrupt APKs were provided as-is and we cannot modify them to comply with instant mode") 46 public class CorruptApkTests extends DeviceTestCase implements IBuildReceiver { 47 48 private static final String CORRUPT_APK_PACKAGE_NAME = "android.content.cts.corruptapk"; 49 private static final String FAIL_COMPRESSED_ARSC_PLATFORM_CONFIG_ID = "132742131"; 50 private static final String COMPRESSED_ARSC_Q = "CtsCorruptApkTests_Compressed_Q.apk"; 51 private static final String COMPRESSED_ARSC_R = "CtsCorruptApkTests_Compressed_R.apk"; 52 private static final String UNALIGNED_ARSC_Q = "CtsCorruptApkTests_Unaligned_Q.apk"; 53 private static final String UNALIGNED_ARSC_R = "CtsCorruptApkTests_Unaligned_R.apk"; 54 55 private IBuildInfo mBuildInfo; 56 57 /** A container for information about the system_server process. */ 58 private class SystemServerInformation { 59 final long mPid; 60 final long mStartTime; 61 SystemServerInformation(long pid, long startTime)62 SystemServerInformation(long pid, long startTime) { 63 this.mPid = pid; 64 this.mStartTime = startTime; 65 } 66 67 @Override equals(Object actual)68 public boolean equals(Object actual) { 69 return (actual instanceof SystemServerInformation) 70 && mPid == ((SystemServerInformation) actual).mPid 71 && mStartTime == ((SystemServerInformation) actual).mStartTime; 72 } 73 } 74 75 /** Retrieves the process id and elapsed run time of system_server. */ retrieveInfo()76 private SystemServerInformation retrieveInfo() throws DeviceNotAvailableException { 77 final ITestDevice device = getDevice(); 78 79 // Retrieve the process id of system_server 80 String pidResult = device.executeShellCommand("pidof system_server").trim(); 81 assertNotNull("Failed to retrieve pid of system_server", pidResult); 82 long pid = 0; 83 try { 84 pid = Long.parseLong(pidResult); 85 } catch (NumberFormatException | IndexOutOfBoundsException e) { 86 fail("Unable to parse pid of system_server '" + pidResult + "'"); 87 } 88 89 // Retrieve the start time of system_server 90 long startTime = 0; 91 String pidStats = device.executeShellCommand("cat /proc/" + pid + "/stat"); 92 assertNotNull("Failed to retrieve stat of system_server with pid '" + pid + "'", pidStats); 93 try { 94 String startTimeJiffies = pidStats.split("\\s+")[21]; 95 startTime = Long.parseLong(startTimeJiffies); 96 } catch (NumberFormatException | IndexOutOfBoundsException e) { 97 fail("Unable to parse system_server stat file '" + pidStats + "'"); 98 } 99 100 return new SystemServerInformation(pid, startTime); 101 } 102 103 @Override setBuild(IBuildInfo buildInfo)104 public void setBuild(IBuildInfo buildInfo) { 105 mBuildInfo = buildInfo; 106 } 107 108 /** Uninstall any test APKs already present on device. */ uninstallApks()109 private void uninstallApks() throws DeviceNotAvailableException { 110 final ITestDevice device = getDevice(); 111 device.uninstallPackage("com.android.appsecurity.b71360999"); 112 device.uninstallPackage("com.android.appsecurity.b71361168"); 113 device.uninstallPackage("com.android.appsecurity.b79488511"); 114 // WARNING: PlatformCompat overrides for package parsing changes must be reset before the 115 // package is uninstalled because overrides cannot be reset if the package is not present. 116 device.executeShellCommand("am compat reset-all " + CORRUPT_APK_PACKAGE_NAME); 117 device.uninstallPackage(CORRUPT_APK_PACKAGE_NAME); 118 } 119 120 @Before 121 @Override setUp()122 public void setUp() throws Exception { 123 super.setUp(); 124 uninstallApks(); 125 } 126 127 @After 128 @Override tearDown()129 public void tearDown() throws Exception { 130 super.tearDown(); 131 uninstallApks(); 132 } 133 install(String apk)134 private String install(String apk) throws DeviceNotAvailableException, FileNotFoundException { 135 return getDevice().installPackage( 136 new CompatibilityBuildHelper(mBuildInfo).getTestFile(apk), 137 true /* reinstall */); 138 } 139 140 /** 141 * Asserts that installing the application does not cause a native error causing system_server 142 * to crash (typically the result of a buffer overflow or an out-of-bounds read). 143 */ assertInstallDoesNotCrashSystem(String apk)144 private void assertInstallDoesNotCrashSystem(String apk) throws Exception { 145 SystemServerInformation beforeInfo = retrieveInfo(); 146 147 final String result = install(apk); 148 CLog.logAndDisplay(LogLevel.INFO, "Result: '" + result + "'"); 149 if (result != null) { 150 assertFalse("Install package segmentation faulted", 151 result.toLowerCase().contains("segmentation fault")); 152 } 153 154 assertEquals("system_server restarted", beforeInfo, retrieveInfo()); 155 } 156 157 /** Installing the APK described in b/71360999 must not crash the device. */ testSafeInstallOfCorruptAPK_b71360999()158 public void testSafeInstallOfCorruptAPK_b71360999() throws Exception { 159 assertInstallDoesNotCrashSystem("CtsCorruptApkTests_b71360999.apk"); 160 } 161 162 /** Installing the APK described in b/71361168 must not crash the device. */ testSafeInstallOfCorruptAPK_b71361168()163 public void testSafeInstallOfCorruptAPK_b71361168() throws Exception { 164 assertInstallDoesNotCrashSystem("CtsCorruptApkTests_b71361168.apk"); 165 } 166 167 /** Installing the APK described in b/79488511 must not crash the device. */ testSafeInstallOfCorruptAPK_b79488511()168 public void testSafeInstallOfCorruptAPK_b79488511() throws Exception { 169 assertInstallDoesNotCrashSystem("CtsCorruptApkTests_b79488511.apk"); 170 } 171 172 /** APKs that target pre-R and have a compressed resources.arsc can be installed. */ testFailInstallCompressedARSC_Q()173 public void testFailInstallCompressedARSC_Q() throws Exception { 174 assertNull(install(COMPRESSED_ARSC_Q)); 175 } 176 testFailInstallCompressedARSC_Q_PlatformConfig_enabled()177 public void testFailInstallCompressedARSC_Q_PlatformConfig_enabled() throws Exception { 178 assertNull(install(COMPRESSED_ARSC_Q)); 179 getDevice().executeShellCommand(String.format("am compat enable %s %s", 180 FAIL_COMPRESSED_ARSC_PLATFORM_CONFIG_ID, CORRUPT_APK_PACKAGE_NAME)); 181 assertNotNull(install(COMPRESSED_ARSC_Q)); 182 } 183 184 /** APKs that target R+ and have a compressed resources.arsc must not be installed. */ testFailInstallCompressedARSC_R()185 public void testFailInstallCompressedARSC_R() throws Exception { 186 assertNotNull(install(COMPRESSED_ARSC_R)); 187 } 188 189 /** APKs that target pre-R and have a unaligned resources.arsc can be installed. */ testFailInstallUnalignedARSC_Q()190 public void testFailInstallUnalignedARSC_Q() throws Exception { 191 assertNull(install(UNALIGNED_ARSC_Q)); 192 } 193 testFailInstallUnalignedARSC_Q_PlatformConfig_enabled()194 public void testFailInstallUnalignedARSC_Q_PlatformConfig_enabled() throws Exception { 195 assertNull(install(UNALIGNED_ARSC_Q)); 196 getDevice().executeShellCommand(String.format("am compat enable %s %s", 197 FAIL_COMPRESSED_ARSC_PLATFORM_CONFIG_ID, CORRUPT_APK_PACKAGE_NAME)); 198 assertNotNull(install(UNALIGNED_ARSC_Q)); 199 } 200 201 /** APKs that target R+ and have a unaligned resources.arsc must not be installed. */ testFailInstallUnalignedARSC_R()202 public void testFailInstallUnalignedARSC_R() throws Exception { 203 assertNotNull(install(UNALIGNED_ARSC_R)); 204 } 205 }