Merge "Assert required kernel security options are enabled" into pi-dev
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/CpuFeatures.java b/common/host-side/util/src/com/android/compatibility/common/util/CpuFeatures.java
index 5f7fae2..7940636 100644
--- a/common/host-side/util/src/com/android/compatibility/common/util/CpuFeatures.java
+++ b/common/host-side/util/src/com/android/compatibility/common/util/CpuFeatures.java
@@ -25,14 +25,25 @@
  */
 public class CpuFeatures {
 
+    private static String uname(ITestDevice device) throws DeviceNotAvailableException {
+        CollectingOutputReceiver Out = new CollectingOutputReceiver();
+        device.executeShellCommand("uname -m", Out);
+        return Out.getOutput().trim();
+    }
+
     /**
      * Return true if architecture is arm64.
      */
     public static boolean isArm64(ITestDevice device) throws DeviceNotAvailableException {
 
-        CollectingOutputReceiver Out = new CollectingOutputReceiver();
-        device.executeShellCommand("uname -m", Out);
-        String arch = Out.getOutput().trim();
-        return arch.contains("aarch64");
+        return uname(device).contains("aarch64");
+    }
+
+    /**
+     * Return true if architecture is arm32.
+     */
+    public static boolean isArm32(ITestDevice device) throws DeviceNotAvailableException {
+
+        return uname(device).contains("armv7");
     }
 }
diff --git a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
new file mode 100644
index 0000000..0619ad1
--- /dev/null
+++ b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.security.cts;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.compatibility.common.util.CddTest;
+import com.android.compatibility.common.util.CpuFeatures;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.lang.String;
+import java.util.stream.Collectors;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * Host-side kernel config tests.
+ *
+ * These tests analyze /proc/config.gz to verify that certain kernel config options are set.
+ */
+public class KernelConfigTest extends DeviceTestCase implements IBuildReceiver, IDeviceTest {
+
+    private static final Map<ITestDevice, HashSet<String>> cachedConfigGzSet = new HashMap<>(1);
+
+    private HashSet<String> configSet;
+
+    private ITestDevice mDevice;
+    private IBuildInfo mBuild;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setBuild(IBuildInfo build) {
+        mBuild = build;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setDevice(ITestDevice device) {
+        super.setDevice(device);
+        mDevice = device;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        configSet = getDeviceConfig(mDevice, cachedConfigGzSet);
+    }
+
+    /*
+     * IMPLEMENTATION DETAILS: Cache the configurations from /proc/config.gz on per-device basis
+     * in case CTS is being run against multiple devices at the same time. This speeds up testing
+     * by avoiding pulling/parsing the config file for each individual test
+     */
+    private static HashSet<String> getDeviceConfig(ITestDevice device,
+            Map<ITestDevice, HashSet<String>> cache) throws Exception {
+        if (!device.doesFileExist("/proc/config.gz")){
+            throw new Exception();
+        }
+        HashSet<String> set;
+        synchronized (cache) {
+            set = cache.get(device);
+        }
+        if (set != null) {
+            return set;
+        }
+        File file = File.createTempFile("config.gz", ".tmp");
+        file.deleteOnExit();
+        device.pullFile("/proc/config.gz", file);
+
+        BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(file))));
+        set = new HashSet<String>(reader.lines().collect(Collectors.toList()));
+
+        synchronized (cache) {
+            cache.put(device, set);
+        }
+        return set;
+    }
+
+    /**
+     * Test that the kernel has Stack Protector Strong enabled.
+     *
+     * @throws Exception
+     */
+    @CddTest(requirement="9.7")
+    public void testConfigStackProtectorStrong() throws Exception {
+        assertTrue("Linux kernel must have Stack Protector enabled: " +
+                "CONFIG_CC_STACKPROTECTOR_STRONG=y",
+                configSet.contains("CONFIG_CC_STACKPROTECTOR_STRONG=y"));
+    }
+
+    /**
+     * Test that the kernel's executable code is read-only, read-only data is non-executable and
+     * non-writable, and writable data is non-executable.
+     *
+     * @throws Exception
+     */
+    @CddTest(requirement="9.7")
+    public void testConfigROData() throws Exception {
+        assertTrue("Linux kernel must have RO data enabled: " +
+                "CONFIG_DEBUG_RODATA=y or CONFIG_STRICT_KERNEL_RWX=y",
+                configSet.contains("CONFIG_DEBUG_RODATA=y") ||
+                configSet.contains("CONFIG_STRICT_KERNEL_RWX=y"));
+
+        if (configSet.contains("CONFIG_MODULES=y")) {
+            assertTrue("Linux kernel modules must also have RO data enabled: " +
+                    "CONFIG_DEBUG_SET_MODULE_RONX=y or CONFIG_STRICT_MODULE_RWX=y",
+                    configSet.contains("CONFIG_DEBUG_SET_MODULE_RONX=y") ||
+                    configSet.contains("CONFIG_STRICT_MODULE_RWX=y"));
+        }
+    }
+
+    /**
+     * Test that the kernel implements static and dynamic object size bounds checking of copies
+     * between user-space and kernel-space.
+     *
+     * @throws Exception
+     */
+    @CddTest(requirement="9.7")
+    public void testConfigHardenedUsercopy() throws Exception {
+        assertTrue("Linux kernel must have Hardened Usercopy enabled: CONFIG_HARDENED_USERCOPY=y",
+                configSet.contains("CONFIG_HARDENED_USERCOPY=y"));
+    }
+
+    /**
+     * Test that the kernel has PAN emulation enabled from architectures that support it.
+     *
+     * @throws Exception
+     */
+    @CddTest(requirement="9.7")
+    public void testConfigPAN() throws Exception {
+        if (CpuFeatures.isArm64(mDevice)) {
+            assertTrue("Linux kernel must have PAN emulation enabled: " +
+                    "CONFIG_ARM64_SW_TTBR0_PAN=y or CONFIG_ARM64_PAN=y",
+                    (configSet.contains("CONFIG_ARM64_SW_TTBR0_PAN=y") ||
+                    configSet.contains("CONFIG_ARM64_PAN=y")));
+        } else if (CpuFeatures.isArm32(mDevice)) {
+            assertTrue("Linux kernel must have PAN emulation enabled: CONFIG_CPU_SW_DOMAIN_PAN=y",
+                    configSet.contains("CONFIG_CPU_SW_DOMAIN_PAN=y"));
+        }
+    }
+}