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 
40 /**
41  * Set of tests that verify that corrupt APKs are properly rejected by PackageManager and
42  * do not cause the system to crash.
43  */
44 @AppModeFull(reason = "the corrupt APKs were provided as-is and we cannot modify them to comply with instant mode")
45 public class CorruptApkTests extends DeviceTestCase implements IBuildReceiver {
46 
47     private IBuildInfo mBuildInfo;
48 
49     /** A container for information about the system_server process. */
50     private class SystemServerInformation {
51         final long mPid;
52         final long mStartTime;
53 
SystemServerInformation(long pid, long startTime)54         SystemServerInformation(long pid, long startTime) {
55             this.mPid = pid;
56             this.mStartTime = startTime;
57         }
58 
59         @Override
equals(Object actual)60         public boolean equals(Object actual) {
61             return (actual instanceof SystemServerInformation)
62                 && mPid == ((SystemServerInformation) actual).mPid
63                 && mStartTime == ((SystemServerInformation) actual).mStartTime;
64         }
65     }
66 
67     /** Retrieves the process id and elapsed run time of system_server. */
retrieveInfo()68     private SystemServerInformation retrieveInfo() throws DeviceNotAvailableException {
69         ITestDevice device = getDevice();
70 
71         // Retrieve the process id of system_server
72         String pidResult = device.executeShellCommand("pidof system_server").trim();
73         assertNotNull("Failed to retrieve pid of system_server", pidResult);
74         long pid = 0;
75         try {
76             pid = Long.parseLong(pidResult);
77         } catch (NumberFormatException | IndexOutOfBoundsException e) {
78             fail("Unable to parse pid of system_server '" + pidResult + "'");
79         }
80 
81         // Retrieve the start time of system_server
82         long startTime = 0;
83         String pidStats = device.executeShellCommand("cat /proc/" + pid + "/stat");
84         assertNotNull("Failed to retrieve stat of system_server with pid '" + pid + "'", pidStats);
85         try {
86             String startTimeJiffies = pidStats.split("\\s+")[21];
87             startTime = Long.parseLong(startTimeJiffies);
88         } catch (NumberFormatException | IndexOutOfBoundsException e) {
89             fail("Unable to parse system_server stat file '" + pidStats + "'");
90         }
91 
92         return new SystemServerInformation(pid, startTime);
93     }
94 
95     @Override
setBuild(IBuildInfo buildInfo)96     public void setBuild(IBuildInfo buildInfo) {
97         mBuildInfo = buildInfo;
98     }
99 
100    /** Uninstall any test APKs already present on device. */
uninstallApks()101     private void uninstallApks() throws DeviceNotAvailableException {
102         ITestDevice device = getDevice();
103         device.uninstallPackage("com.android.appsecurity.b71360999");
104         device.uninstallPackage("com.android.appsecurity.b71361168");
105         device.uninstallPackage("com.android.appsecurity.b79488511");
106     }
107 
108     @Before
109     @Override
setUp()110     public void setUp() throws Exception {
111         super.setUp();
112         uninstallApks();
113     }
114 
115     @After
116     @Override
tearDown()117     public void tearDown() throws Exception {
118         super.tearDown();
119         uninstallApks();
120     }
121 
122     /**
123      * Asserts that installing the application does not cause a native error causing system_server
124      * to crash (typically the result of a buffer overflow or an out-of-bounds read).
125      */
assertInstallDoesNotCrashSystem(String apk)126     private void assertInstallDoesNotCrashSystem(String apk) throws Exception {
127         SystemServerInformation beforeInfo = retrieveInfo();
128 
129         final String result = getDevice().installPackage(
130                 new CompatibilityBuildHelper(mBuildInfo).getTestFile(apk),
131                 false /*reinstall*/);
132         CLog.logAndDisplay(LogLevel.INFO, "Result: '" + result + "'");
133         if (result != null) {
134             assertFalse("Install package segmentation faulted",
135                 result.toLowerCase().contains("segmentation fault"));
136         }
137 
138         assertEquals("system_server restarted", beforeInfo, retrieveInfo());
139     }
140 
141     /** Tests that installing the APK described in b/71360999 does not crash the device. */
testSafeInstallOfCorruptAPK_b71360999()142     public void testSafeInstallOfCorruptAPK_b71360999() throws Exception {
143         assertInstallDoesNotCrashSystem("CtsCorruptApkTests_b71360999.apk");
144     }
145 
146     /** Tests that installing the APK described in b/71361168 does not crash the device. */
testSafeInstallOfCorruptAPK_b71361168()147     public void testSafeInstallOfCorruptAPK_b71361168() throws Exception {
148         assertInstallDoesNotCrashSystem("CtsCorruptApkTests_b71361168.apk");
149     }
150 
151     /** Tests that installing the APK described in b/79488511 does not crash the device. */
testSafeInstallOfCorruptAPK_b79488511()152     public void testSafeInstallOfCorruptAPK_b79488511() throws Exception {
153         assertInstallDoesNotCrashSystem("CtsCorruptApkTests_b79488511.apk");
154     }
155 }