RESTRICT AUTOMERGE: CTS: Created ResStringPool security tests

Ensures that APKs with malformed ResStringPool data are not successfully
installed.

Bug: 71361168
Bug: 71360999
Test: run cts -m CtsAppSecurityHostTestCases \
          -t android.appsecurity.cts.CorruptApkTests

Change-Id: I018f79f33b2f6a9b01dd6d5d35d595d3d70be87b
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ValidateTestsAbi.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ValidateTestsAbi.java
index ebe50a9..297ccfc 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ValidateTestsAbi.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ValidateTestsAbi.java
@@ -44,11 +44,19 @@
 @RunWith(JUnit4.class)
 public class ValidateTestsAbi {
 
-    /**
-     *  This particular module is shipping all it's dependencies in all abis with prebuilt stuff.
-     *  Excluding it for now to have the test setup.
-     */
-    private static final String MODULE_EXCEPTION = "CtsSplitApp";
+    private static final Set<String> MODULE_EXCEPTIONS = new HashSet<>();
+    static {
+        /**
+         *  This particular module is shipping all it's dependencies in all abis with prebuilt stuff.
+         *  Excluding it for now to have the test setup.
+         */
+        MODULE_EXCEPTIONS.add("CtsSplitApp");
+
+        /**
+         *  This module tests for security vulnerabilities when installing attacker-devised APKs.
+         */
+        MODULE_EXCEPTIONS.add("CtsCorruptApkTests");
+    }
 
     private static final Set<String> BINARY_EXCEPTIONS = new HashSet<>();
     static {
@@ -74,13 +82,13 @@
         File[] listApks = testcases.listFiles(new FilenameFilter() {
             @Override
             public boolean accept(File dir, String name) {
-                if (name.startsWith(MODULE_EXCEPTION)) {
-                    return false;
+                for (String module : MODULE_EXCEPTIONS) {
+                    if (name.startsWith(module)) {
+                        return false;
+                    }
                 }
-                if (name.endsWith(".apk")) {
-                    return true;
-                }
-                return false;
+
+                return name.endsWith(".apk");
             }
         });
         assertTrue(listApks.length > 0);
diff --git a/hostsidetests/appsecurity/Android.mk b/hostsidetests/appsecurity/Android.mk
index 975bcb5..0ef5da3 100644
--- a/hostsidetests/appsecurity/Android.mk
+++ b/hostsidetests/appsecurity/Android.mk
@@ -30,6 +30,10 @@
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
+LOCAL_REQUIRED_MODULES := \
+	CtsCorruptApkTests_b71360999 \
+	CtsCorruptApkTests_b71361168
+
 include $(BUILD_CTS_HOST_JAVA_LIBRARY)
 
 # Build the test APKs using their own makefiles
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
new file mode 100644
index 0000000..bcf67b0
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2018 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.appsecurity.cts;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.FileUtil;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.File;
+
+/**
+ * Set of tests that verify that corrupt APKs are properly rejected by PackageManager and
+ * do not cause the system to crash.
+ */
+public class CorruptApkTests extends DeviceTestCase implements IBuildReceiver {
+    private final String B71360999_PKG = "com.android.appsecurity.b71360999";
+    private final String B71361168_PKG = "com.example.helloworld";
+
+    private IBuildInfo mBuildInfo;
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mBuildInfo = buildInfo;
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        uninstall(B71360999_PKG);
+        uninstall(B71361168_PKG);
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        uninstall(B71360999_PKG);
+        uninstall(B71361168_PKG);
+    }
+
+    /** Uninstall the apk if the test failed previously. */
+    public void uninstall(String pkg) throws Exception {
+        ITestDevice device = getDevice();
+        if (device.getInstalledPackageNames().contains(pkg)) {
+            device.uninstallPackage(pkg);
+        }
+    }
+
+    /**
+     * Tests that apks described in b/71360999 do not install successfully nor cause
+     */
+    public void testFailToInstallCorruptStringPoolHeader_b71360999() throws Exception {
+        final String APK_PATH = "CtsCorruptApkTests_b71360999.apk";
+        assertFailsToInstall(APK_PATH, B71360999_PKG);
+    }
+
+    /**
+     * Tests that apks described in b/71361168 do not install successfully.
+     */
+    public void testFailToInstallCorruptStringPoolHeader_b71361168() throws Exception {
+        final String APK_PATH = "CtsCorruptApkTests_b71361168.apk";
+        assertFailsToInstall(APK_PATH, B71361168_PKG);
+    }
+
+    /**
+     * Assert that the app fails to install and the reason for failing is not caused by a buffer
+     * overflow nor a out of bounds read.
+     **/
+    private void assertFailsToInstall(String filename, String pkg) throws Exception {
+        ITestDevice device = getDevice();
+        device.clearLogcat();
+
+        final String result = device.installPackage(
+                new CompatibilityBuildHelper(mBuildInfo).getTestFile(filename),
+                true /*reinstall*/);
+
+        assertNotNull(result);
+        assertFalse(result.isEmpty());
+        assertFalse(device.getInstalledPackageNames().contains(pkg));
+
+        // This catches if the device fails to install the app because a segmentation fault
+        // or out of bounds read created by the bug occurs
+        File tmpTxtFile = null;
+        InputStreamSource source = device.getLogcat(200 * 1024);
+        try {
+            assertNotNull(source);
+            tmpTxtFile = FileUtil.createTempFile("logcat", ".txt");
+            FileUtil.writeToFile(source.createInputStream(), tmpTxtFile);
+            String s = FileUtil.readStringFromFile(tmpTxtFile);
+            assertFalse(s.contains("SIGSEGV"));
+            assertFalse(s.contains("==ERROR"));
+        } finally {
+            source.close();
+            if (tmpTxtFile != null) {
+                FileUtil.deleteFile(tmpTxtFile);
+            }
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/CorruptApkTests/Android.mk b/hostsidetests/appsecurity/test-apps/CorruptApkTests/Android.mk
new file mode 100644
index 0000000..feaaa03
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/CorruptApkTests/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2018 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CtsCorruptApkTests_b71360999
+LOCAL_MODULE_CLASS := APPS
+LOCAL_SRC_FILES := b71360999.apk
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_CERTIFICATE := PRESIGNED
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CtsCorruptApkTests_b71361168
+LOCAL_MODULE_CLASS := APPS
+LOCAL_SRC_FILES := b71361168.apk
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_CERTIFICATE := PRESIGNED
+include $(BUILD_PREBUILT)
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/CorruptApkTests/b71360999.apk b/hostsidetests/appsecurity/test-apps/CorruptApkTests/b71360999.apk
new file mode 100644
index 0000000..55baa66
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/CorruptApkTests/b71360999.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/CorruptApkTests/b71361168.apk b/hostsidetests/appsecurity/test-apps/CorruptApkTests/b71361168.apk
new file mode 100644
index 0000000..ef1e2bf
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/CorruptApkTests/b71361168.apk
Binary files differ