• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy 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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.security.cts;
18 
19 import com.android.tradefed.build.IBuildInfo;
20 import com.android.tradefed.device.ITestDevice;
21 import com.android.tradefed.testtype.DeviceTestCase;
22 import com.android.tradefed.testtype.IBuildReceiver;
23 import com.android.tradefed.testtype.IDeviceTest;
24 import com.android.compatibility.common.util.CddTest;
25 import com.android.compatibility.common.util.CpuFeatures;
26 import com.android.compatibility.common.util.PropertyUtil;
27 
28 import java.io.BufferedReader;
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.io.InputStreamReader;
32 import java.lang.String;
33 import java.util.stream.Collectors;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.Map;
37 import java.util.zip.GZIPInputStream;
38 
39 /**
40  * Host-side kernel config tests.
41  *
42  * These tests analyze /proc/config.gz to verify that certain kernel config options are set.
43  */
44 public class KernelConfigTest extends DeviceTestCase implements IBuildReceiver, IDeviceTest {
45 
46     private static final Map<ITestDevice, HashSet<String>> cachedConfigGzSet = new HashMap<>(1);
47 
48     private HashSet<String> configSet;
49 
50     private ITestDevice mDevice;
51     private IBuildInfo mBuild;
52 
53     /**
54      * {@inheritDoc}
55      */
56     @Override
setBuild(IBuildInfo build)57     public void setBuild(IBuildInfo build) {
58         mBuild = build;
59     }
60 
61     /**
62      * {@inheritDoc}
63      */
64     @Override
setDevice(ITestDevice device)65     public void setDevice(ITestDevice device) {
66         super.setDevice(device);
67         mDevice = device;
68     }
69 
70     @Override
setUp()71     protected void setUp() throws Exception {
72         super.setUp();
73         configSet = getDeviceConfig(mDevice, cachedConfigGzSet);
74     }
75 
76     /*
77      * IMPLEMENTATION DETAILS: Cache the configurations from /proc/config.gz on per-device basis
78      * in case CTS is being run against multiple devices at the same time. This speeds up testing
79      * by avoiding pulling/parsing the config file for each individual test
80      */
getDeviceConfig(ITestDevice device, Map<ITestDevice, HashSet<String>> cache)81     private static HashSet<String> getDeviceConfig(ITestDevice device,
82             Map<ITestDevice, HashSet<String>> cache) throws Exception {
83         if (!device.doesFileExist("/proc/config.gz")){
84             throw new Exception();
85         }
86         HashSet<String> set;
87         synchronized (cache) {
88             set = cache.get(device);
89         }
90         if (set != null) {
91             return set;
92         }
93         File file = File.createTempFile("config.gz", ".tmp");
94         file.deleteOnExit();
95         device.pullFile("/proc/config.gz", file);
96 
97         BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(file))));
98         set = new HashSet<String>(reader.lines().collect(Collectors.toList()));
99 
100         synchronized (cache) {
101             cache.put(device, set);
102         }
103         return set;
104     }
105 
106     /**
107      * Test that the kernel has Stack Protector Strong enabled.
108      *
109      * @throws Exception
110      */
111     @CddTest(requirement="9.7")
testConfigStackProtectorStrong()112     public void testConfigStackProtectorStrong() throws Exception {
113         assertTrue("Linux kernel must have Stack Protector enabled: " +
114                 "CONFIG_STACKPROTECTOR_STRONG=y or CONFIG_CC_STACKPROTECTOR_STRONG=y",
115                 configSet.contains("CONFIG_STACKPROTECTOR_STRONG=y") ||
116                 configSet.contains("CONFIG_CC_STACKPROTECTOR_STRONG=y"));
117     }
118 
119     /**
120      * Test that the kernel's executable code is read-only, read-only data is non-executable and
121      * non-writable, and writable data is non-executable.
122      *
123      * @throws Exception
124      */
125     @CddTest(requirement="9.7")
testConfigROData()126     public void testConfigROData() throws Exception {
127         if (configSet.contains("CONFIG_UH_RKP=y"))
128             return;
129 
130         assertTrue("Linux kernel must have RO data enabled: " +
131                 "CONFIG_DEBUG_RODATA=y or CONFIG_STRICT_KERNEL_RWX=y",
132                 configSet.contains("CONFIG_DEBUG_RODATA=y") ||
133                 configSet.contains("CONFIG_STRICT_KERNEL_RWX=y"));
134 
135         if (configSet.contains("CONFIG_MODULES=y")) {
136             assertTrue("Linux kernel modules must also have RO data enabled: " +
137                     "CONFIG_DEBUG_SET_MODULE_RONX=y or CONFIG_STRICT_MODULE_RWX=y",
138                     configSet.contains("CONFIG_DEBUG_SET_MODULE_RONX=y") ||
139                     configSet.contains("CONFIG_STRICT_MODULE_RWX=y"));
140         }
141     }
142 
143     /**
144      * Test that the kernel implements static and dynamic object size bounds checking of copies
145      * between user-space and kernel-space.
146      *
147      * @throws Exception
148      */
149     @CddTest(requirement="9.7")
testConfigHardenedUsercopy()150     public void testConfigHardenedUsercopy() throws Exception {
151         if (PropertyUtil.getFirstApiLevel(mDevice) < 28) {
152             return;
153         }
154 
155         assertTrue("Linux kernel must have Hardened Usercopy enabled: CONFIG_HARDENED_USERCOPY=y",
156                 configSet.contains("CONFIG_HARDENED_USERCOPY=y"));
157     }
158 
159     /**
160      * Test that the kernel has PAN emulation enabled from architectures that support it.
161      *
162      * @throws Exception
163      */
164     @CddTest(requirement="9.7")
testConfigPAN()165     public void testConfigPAN() throws Exception {
166         if (PropertyUtil.getFirstApiLevel(mDevice) < 28) {
167             return;
168         }
169 
170         if (CpuFeatures.isArm64(mDevice)) {
171             assertTrue("Linux kernel must have PAN emulation enabled: " +
172                     "CONFIG_ARM64_SW_TTBR0_PAN=y or CONFIG_ARM64_PAN=y",
173                     (configSet.contains("CONFIG_ARM64_SW_TTBR0_PAN=y") ||
174                     configSet.contains("CONFIG_ARM64_PAN=y")));
175         } else if (CpuFeatures.isArm32(mDevice)) {
176             assertTrue("Linux kernel must have PAN emulation enabled: CONFIG_CPU_SW_DOMAIN_PAN=y",
177                     configSet.contains("CONFIG_CPU_SW_DOMAIN_PAN=y"));
178         }
179     }
180 
getHardware()181     private String getHardware() throws Exception {
182         String hardware = "DEFAULT";
183         String cpuInfo = mDevice.pullFileContents("/proc/cpuinfo");
184 
185         for (String line : cpuInfo.split("\n")) {
186             /* Qualcomm SoCs */
187             if (line.startsWith("Hardware")) {
188                 String[] hardwareLine = line.split(" ");
189                 hardware = hardwareLine[hardwareLine.length - 1];
190                 break;
191             }
192         }
193         /* TODO lookup other hardware as we get exemption requests. */
194         return hardwareMitigations.containsKey(hardware) ? hardware : "DEFAULT";
195     }
196 
doesFileExist(String filePath)197     private boolean doesFileExist(String filePath) throws Exception {
198         String lsGrep = mDevice.executeShellCommand(String.format("ls \"%s\"", filePath));
199         return lsGrep.trim().equals(filePath);
200     }
201 
202     private Map<String, String[]> hardwareMitigations = new HashMap<String, String[]>() {
203     {
204         put("Kirin980", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
205         put("Kirin970", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
206         put("Kirin810", null);
207         put("Kirin710", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
208         put("SM8150", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
209         put("DEFAULT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y",
210             "CONFIG_UNMAP_KERNEL_AT_EL0=y"});
211     }};
212 
lookupMitigations()213     private String[] lookupMitigations() throws Exception {
214         return hardwareMitigations.get(getHardware());
215     }
216 
217     /**
218      * Test that the kernel has Spectre/Meltdown mitigations for architectures and kernel versions
219      * that support it. Exempt platforms which are known to not be vulnerable.
220      *
221      * @throws Exception
222      */
223     @CddTest(requirement="9.7")
testConfigHardwareMitigations()224     public void testConfigHardwareMitigations() throws Exception {
225         if (PropertyUtil.getFirstApiLevel(mDevice) < 28) {
226             return;
227         }
228 
229         if (CpuFeatures.isArm64(mDevice) && !CpuFeatures.kernelVersionLessThan(mDevice, 4, 4)) {
230             for (String mitigation : lookupMitigations()) {
231                 assertTrue("Linux kernel must have " + mitigation + " enabled.",
232                         configSet.contains(mitigation));
233             }
234         } else if (CpuFeatures.isX86(mDevice)) {
235             assertTrue("Linux kernel must have KPTI enabled: CONFIG_PAGE_TABLE_ISOLATION=y",
236                     configSet.contains("CONFIG_PAGE_TABLE_ISOLATION=y"));
237         }
238     }
239 }
240