1 /* 2 * Copyright (C) 2014 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 android.platform.test.annotations.RestrictedBuildTest; 20 21 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 22 import com.android.compatibility.common.util.PropertyUtil; 23 import com.android.tradefed.build.IBuildInfo; 24 import com.android.tradefed.device.CollectingOutputReceiver; 25 import com.android.tradefed.device.DeviceNotAvailableException; 26 import com.android.tradefed.device.ITestDevice; 27 import com.android.tradefed.testtype.DeviceTestCase; 28 import com.android.tradefed.testtype.IBuildReceiver; 29 import com.android.tradefed.testtype.IDeviceTest; 30 31 import com.android.compatibility.common.util.CddTest; 32 33 import java.io.BufferedReader; 34 import java.io.ByteArrayOutputStream; 35 import java.io.File; 36 import java.io.FileReader; 37 import java.io.FileInputStream; 38 import java.io.FileOutputStream; 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.io.InputStreamReader; 42 import java.lang.String; 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.Collections; 46 import java.util.HashMap; 47 import java.util.HashSet; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.regex.Matcher; 51 import java.util.regex.Pattern; 52 import java.util.Scanner; 53 import java.util.Set; 54 55 import javax.xml.parsers.DocumentBuilder; 56 import javax.xml.parsers.DocumentBuilderFactory; 57 import org.w3c.dom.Document; 58 import org.w3c.dom.Element; 59 60 /** 61 * Host-side SELinux tests. 62 * 63 * These tests analyze the policy file in use on the subject device directly or 64 * run as the shell user to evaluate aspects of the state of SELinux on the test 65 * device which otherwise would not be available to a normal apk. 66 */ 67 public class SELinuxHostTest extends DeviceTestCase implements IBuildReceiver, IDeviceTest { 68 69 private static final Map<ITestDevice, File> cachedDevicePolicyFiles = new HashMap<>(1); 70 private static final Map<ITestDevice, File> cachedDevicePlatFcFiles = new HashMap<>(1); 71 private static final Map<ITestDevice, File> cachedDeviceNonplatFcFiles = new HashMap<>(1); 72 private static final Map<ITestDevice, File> cachedDeviceVendorManifest = new HashMap<>(1); 73 private static final Map<ITestDevice, File> cachedDeviceSystemPolicy = new HashMap<>(1); 74 75 private File sepolicyAnalyze; 76 private File checkSeapp; 77 private File checkFc; 78 private File aospSeappFile; 79 private File aospFcFile; 80 private File aospPcFile; 81 private File aospSvcFile; 82 private File devicePolicyFile; 83 private File devicePlatSeappFile; 84 private File deviceNonplatSeappFile; 85 private File devicePlatFcFile; 86 private File deviceNonplatFcFile; 87 private File devicePcFile; 88 private File deviceSvcFile; 89 private File seappNeverAllowFile; 90 private File libsepolwrap; 91 private File libcpp; 92 private File sepolicyTests; 93 94 private IBuildInfo mBuild; 95 96 /** 97 * A reference to the device under test. 98 */ 99 private ITestDevice mDevice; 100 101 /** 102 * {@inheritDoc} 103 */ 104 @Override setBuild(IBuildInfo build)105 public void setBuild(IBuildInfo build) { 106 mBuild = build; 107 } 108 109 /** 110 * {@inheritDoc} 111 */ 112 @Override setDevice(ITestDevice device)113 public void setDevice(ITestDevice device) { 114 super.setDevice(device); 115 mDevice = device; 116 } 117 copyResourceToTempFile(String resName)118 public static File copyResourceToTempFile(String resName) throws IOException { 119 InputStream is = SELinuxHostTest.class.getResourceAsStream(resName); 120 File tempFile = File.createTempFile("SELinuxHostTest", ".tmp"); 121 FileOutputStream os = new FileOutputStream(tempFile); 122 byte[] buf = new byte[1024]; 123 int len; 124 125 while ((len = is.read(buf)) != -1) { 126 os.write(buf, 0, len); 127 } 128 os.flush(); 129 os.close(); 130 tempFile.deleteOnExit(); 131 return tempFile; 132 } 133 appendTo(String dest, String src)134 private static void appendTo(String dest, String src) throws IOException { 135 try (FileInputStream is = new FileInputStream(new File(src)); 136 FileOutputStream os = new FileOutputStream(new File(dest))) { 137 byte[] buf = new byte[1024]; 138 int len; 139 140 while ((len = is.read(buf)) != -1) { 141 os.write(buf, 0, len); 142 } 143 } 144 } 145 146 @Override setUp()147 protected void setUp() throws Exception { 148 super.setUp(); 149 CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild); 150 sepolicyAnalyze = buildHelper.getTestFile("sepolicy-analyze"); 151 sepolicyAnalyze.setExecutable(true); 152 153 devicePolicyFile = getDevicePolicyFile(mDevice); 154 if (isSepolicySplit(mDevice)) { 155 devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles, 156 "/system/etc/selinux/plat_file_contexts", "plat_file_contexts"); 157 if (mDevice.doesFileExist("/vendor/etc/selinux/nonplat_file_contexts")){ 158 // Old nonplat_* naming can be present if a framework-only OTA was done. 159 deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles, 160 "/vendor/etc/selinux/nonplat_file_contexts", "nonplat_file_contexts"); 161 } else { 162 deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles, 163 "/vendor/etc/selinux/vendor_file_contexts", "vendor_file_contexts"); 164 } 165 } else { 166 devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles, 167 "/plat_file_contexts", "plat_file_contexts"); 168 deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles, 169 "/vendor_file_contexts", "vendor_file_contexts"); 170 } 171 } 172 173 /* 174 * IMPLEMENTATION DETAILS: We cache some host-side policy files on per-device basis (in case 175 * CTS supports running against multiple devices at the same time). HashMap is used instead 176 * of WeakHashMap because in the grand scheme of things, keeping ITestDevice and 177 * corresponding File objects from being garbage-collected is not a big deal in CTS. If this 178 * becomes a big deal, this can be switched to WeakHashMap. 179 */ getDeviceFile(ITestDevice device, Map<ITestDevice, File> cache, String deviceFilePath, String tmpFileName)180 private static File getDeviceFile(ITestDevice device, 181 Map<ITestDevice, File> cache, String deviceFilePath, 182 String tmpFileName) throws Exception { 183 if (!device.doesFileExist(deviceFilePath)){ 184 throw new Exception(); 185 } 186 File file; 187 synchronized (cache) { 188 file = cache.get(device); 189 } 190 if (file != null) { 191 return file; 192 } 193 file = File.createTempFile(tmpFileName, ".tmp"); 194 file.deleteOnExit(); 195 device.pullFile(deviceFilePath, file); 196 synchronized (cache) { 197 cache.put(device, file); 198 } 199 return file; 200 } 201 buildSystemPolicy(ITestDevice device, Map<ITestDevice, File> cache, String tmpFileName)202 private static File buildSystemPolicy(ITestDevice device, Map<ITestDevice, File> cache, 203 String tmpFileName) throws Exception { 204 File builtPolicyFile; 205 synchronized (cache) { 206 builtPolicyFile = cache.get(device); 207 } 208 if (builtPolicyFile != null) { 209 return builtPolicyFile; 210 } 211 212 213 builtPolicyFile = File.createTempFile(tmpFileName, ".tmp"); 214 builtPolicyFile.deleteOnExit(); 215 216 File secilc = copyResourceToTempFile("/secilc"); 217 secilc.setExecutable(true); 218 219 File systemSepolicyCilFile = File.createTempFile("plat_sepolicy", ".cil"); 220 systemSepolicyCilFile.deleteOnExit(); 221 222 assertTrue(device.pullFile("/system/etc/selinux/plat_sepolicy.cil", systemSepolicyCilFile)); 223 224 ProcessBuilder pb = new ProcessBuilder( 225 secilc.getAbsolutePath(), 226 "-m", "-M", "true", "-c", "30", 227 "-o", builtPolicyFile.getAbsolutePath(), 228 systemSepolicyCilFile.getAbsolutePath()); 229 pb.redirectOutput(ProcessBuilder.Redirect.PIPE); 230 pb.redirectErrorStream(true); 231 Process p = pb.start(); 232 p.waitFor(); 233 BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream())); 234 String line; 235 StringBuilder errorString = new StringBuilder(); 236 while ((line = result.readLine()) != null) { 237 errorString.append(line); 238 errorString.append("\n"); 239 } 240 assertTrue(errorString.toString(), errorString.length() == 0); 241 242 synchronized (cache) { 243 cache.put(device, builtPolicyFile); 244 } 245 return builtPolicyFile; 246 } 247 248 // NOTE: cts/tools/selinux depends on this method. Rename/change with caution. 249 /** 250 * Returns the host-side file containing the SELinux policy of the device under test. 251 */ getDevicePolicyFile(ITestDevice device)252 public static File getDevicePolicyFile(ITestDevice device) throws Exception { 253 return getDeviceFile(device, cachedDevicePolicyFiles, "/sys/fs/selinux/policy", "sepolicy"); 254 } 255 256 // NOTE: cts/tools/selinux depends on this method. Rename/change with caution. 257 /** 258 * Returns the host-side file containing the system SELinux policy of the device under test. 259 */ getDeviceSystemPolicyFile(ITestDevice device)260 public static File getDeviceSystemPolicyFile(ITestDevice device) throws Exception { 261 return buildSystemPolicy(device, cachedDeviceSystemPolicy, "system_sepolicy"); 262 } 263 264 // NOTE: cts/tools/selinux depends on this method. Rename/change with caution. 265 /** 266 * Returns the major number of sepolicy version of device's vendor implementation. 267 * TODO(b/37999212): Use VINTF object API instead of parsing vendor manifest. 268 */ getVendorSepolicyVersion(ITestDevice device)269 public static int getVendorSepolicyVersion(ITestDevice device) throws Exception { 270 String deviceManifestPath = 271 (device.doesFileExist("/vendor/etc/vintf/manifest.xml")) ? 272 "/vendor/etc/vintf/manifest.xml" : 273 "/vendor/manifest.xml"; 274 File vendorManifestFile = getDeviceFile(device, cachedDeviceVendorManifest, 275 deviceManifestPath, "manifest.xml"); 276 277 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 278 DocumentBuilder db = dbf.newDocumentBuilder(); 279 Document doc = db.parse(vendorManifestFile); 280 Element root = doc.getDocumentElement(); 281 Element sepolicy = (Element) root.getElementsByTagName("sepolicy").item(0); 282 Element version = (Element) sepolicy.getElementsByTagName("version").item(0); 283 String sepolicyVersion = version.getTextContent().split("\\.")[0]; 284 return Integer.parseInt(sepolicyVersion); 285 } 286 287 /** 288 * Tests that the kernel is enforcing selinux policy globally. 289 * 290 * @throws Exception 291 */ 292 @CddTest(requirement="9.7") testGlobalEnforcing()293 public void testGlobalEnforcing() throws Exception { 294 CollectingOutputReceiver out = new CollectingOutputReceiver(); 295 mDevice.executeShellCommand("cat /sys/fs/selinux/enforce", out); 296 assertEquals("SELinux policy is not being enforced!", "1", out.getOutput()); 297 } 298 299 /** 300 * Tests that all domains in the running policy file are in enforcing mode 301 * 302 * @throws Exception 303 */ 304 @CddTest(requirement="9.7") 305 @RestrictedBuildTest testAllDomainsEnforcing()306 public void testAllDomainsEnforcing() throws Exception { 307 308 /* run sepolicy-analyze permissive check on policy file */ 309 ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(), 310 devicePolicyFile.getAbsolutePath(), "permissive"); 311 pb.redirectOutput(ProcessBuilder.Redirect.PIPE); 312 pb.redirectErrorStream(true); 313 Process p = pb.start(); 314 p.waitFor(); 315 BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream())); 316 String line; 317 StringBuilder errorString = new StringBuilder(); 318 while ((line = result.readLine()) != null) { 319 errorString.append(line); 320 errorString.append("\n"); 321 } 322 assertTrue("The following SELinux domains were found to be in permissive mode:\n" 323 + errorString, errorString.length() == 0); 324 } 325 326 /** 327 * Asserts that specified type is not associated with the specified 328 * attribute. 329 * 330 * @param attribute 331 * The attribute name. 332 * @param type 333 * The type name. 334 */ assertNotInAttribute(String attribute, String badtype)335 private void assertNotInAttribute(String attribute, String badtype) throws Exception { 336 Set<String> actualTypes = sepolicyAnalyzeGetTypesAssociatedWithAttribute(attribute); 337 if (actualTypes.contains(badtype)) { 338 fail("Attribute " + attribute + " includes " + badtype); 339 } 340 } 341 readFully(InputStream in)342 private static final byte[] readFully(InputStream in) throws IOException { 343 ByteArrayOutputStream result = new ByteArrayOutputStream(); 344 byte[] buf = new byte[65536]; 345 int chunkSize; 346 while ((chunkSize = in.read(buf)) != -1) { 347 result.write(buf, 0, chunkSize); 348 } 349 return result.toByteArray(); 350 } 351 352 /** 353 * Runs sepolicy-analyze against the device's SELinux policy and returns the set of types 354 * associated with the provided attribute. 355 */ sepolicyAnalyzeGetTypesAssociatedWithAttribute( String attribute)356 private Set<String> sepolicyAnalyzeGetTypesAssociatedWithAttribute( 357 String attribute) throws Exception { 358 ProcessBuilder pb = 359 new ProcessBuilder( 360 sepolicyAnalyze.getAbsolutePath(), 361 devicePolicyFile.getAbsolutePath(), 362 "attribute", 363 attribute); 364 pb.redirectOutput(ProcessBuilder.Redirect.PIPE); 365 pb.redirectErrorStream(true); 366 Process p = pb.start(); 367 int errorCode = p.waitFor(); 368 if (errorCode != 0) { 369 fail("sepolicy-analyze attribute " + attribute + " failed with error code " + errorCode 370 + ": " + new String(readFully(p.getInputStream()))); 371 } 372 try (BufferedReader in = 373 new BufferedReader(new InputStreamReader(p.getInputStream()))) { 374 Set<String> types = new HashSet<>(); 375 String type; 376 while ((type = in.readLine()) != null) { 377 types.add(type.trim()); 378 } 379 return types; 380 } 381 } 382 383 // NOTE: cts/tools/selinux depends on this method. Rename/change with caution. 384 /** 385 * Returns {@code true} if this device is required to be a full Treble device. 386 */ isFullTrebleDevice(ITestDevice device)387 public static boolean isFullTrebleDevice(ITestDevice device) 388 throws DeviceNotAvailableException { 389 return PropertyUtil.getFirstApiLevel(device) > 26; 390 } 391 isFullTrebleDevice()392 private boolean isFullTrebleDevice() throws DeviceNotAvailableException { 393 return isFullTrebleDevice(mDevice); 394 } 395 396 // NOTE: cts/tools/selinux depends on this method. Rename/change with caution. 397 /** 398 * Returns {@code true} if this device is required to enforce compatible property. 399 */ isCompatiblePropertyEnforcedDevice(ITestDevice device)400 public static boolean isCompatiblePropertyEnforcedDevice(ITestDevice device) 401 throws DeviceNotAvailableException { 402 return PropertyUtil.getFirstApiLevel(device) > 27; 403 } 404 405 // NOTE: cts/tools/selinux depends on this method. Rename/change with caution. 406 /** 407 * Returns {@code true} if this device has sepolicy split across different paritions. 408 * This is possible even for devices launched at api level higher than 26. 409 */ isSepolicySplit(ITestDevice device)410 public static boolean isSepolicySplit(ITestDevice device) 411 throws DeviceNotAvailableException { 412 return device.doesFileExist("/system/etc/selinux/plat_file_contexts"); 413 } 414 415 /** 416 * Asserts that no vendor domains are exempted from the prohibition on Binder use. 417 * 418 * <p>NOTE: binder_in_vendor_violators attribute is only there to help bring up Treble devices. 419 * It offers a convenient way to temporarily bypass the prohibition on Binder use in vendor 420 * domains. This attribute must not be used on production Treble devices. 421 */ testNoExemptionsForBinderInVendorBan()422 public void testNoExemptionsForBinderInVendorBan() throws Exception { 423 if (!isFullTrebleDevice()) { 424 return; 425 } 426 427 Set<String> types = 428 sepolicyAnalyzeGetTypesAssociatedWithAttribute("binder_in_vendor_violators"); 429 if (!types.isEmpty()) { 430 List<String> sortedTypes = new ArrayList<>(types); 431 Collections.sort(sortedTypes); 432 fail("Policy exempts vendor domains from ban on Binder: " + sortedTypes); 433 } 434 } 435 436 /** 437 * Asserts that no HAL server domains are exempted from the prohibition of socket use with the 438 * only exceptions for the automotive device type. 439 */ testNoExemptionsForSocketsUseWithinHalServer()440 public void testNoExemptionsForSocketsUseWithinHalServer() throws Exception { 441 if (!isFullTrebleDevice()) { 442 return; 443 } 444 445 if (getDevice().hasFeature("android.hardware.type.automotive")) { 446 return; 447 } 448 449 Set<String> types = sepolicyAnalyzeGetTypesAssociatedWithAttribute( 450 "hal_automotive_socket_exemption"); 451 if (!types.isEmpty()) { 452 List<String> sortedTypes = new ArrayList<>(types); 453 Collections.sort(sortedTypes); 454 fail("Policy exempts domains from ban on socket usage from HAL servers: " 455 + sortedTypes); 456 } 457 } 458 459 /** 460 * Asserts that no domains are exempted from the prohibition on initiating socket communications 461 * between core and vendor domains. 462 * 463 * <p>NOTE: socket_between_core_and_vendor_violators attribute is only there to help bring up 464 * Treble devices. It offers a convenient way to temporarily bypass the prohibition on 465 * initiating socket communications between core and vendor domains. This attribute must not be 466 * used on production Treble devices. 467 */ testNoExemptionsForSocketsBetweenCoreAndVendorBan()468 public void testNoExemptionsForSocketsBetweenCoreAndVendorBan() throws Exception { 469 if (!isFullTrebleDevice()) { 470 return; 471 } 472 473 Set<String> types = 474 sepolicyAnalyzeGetTypesAssociatedWithAttribute( 475 "socket_between_core_and_vendor_violators"); 476 if (!types.isEmpty()) { 477 List<String> sortedTypes = new ArrayList<>(types); 478 Collections.sort(sortedTypes); 479 fail("Policy exempts domains from ban on socket communications between core and" 480 + " vendor: " + sortedTypes); 481 } 482 } 483 484 /** 485 * Asserts that no vendor domains are exempted from the prohibition on directly 486 * executing binaries from /system. 487 * */ testNoExemptionsForVendorExecutingCore()488 public void testNoExemptionsForVendorExecutingCore() throws Exception { 489 if (!isFullTrebleDevice()) { 490 return; 491 } 492 493 Set<String> types = 494 sepolicyAnalyzeGetTypesAssociatedWithAttribute( 495 "vendor_executes_system_violators"); 496 if (!types.isEmpty()) { 497 List<String> sortedTypes = new ArrayList<>(types); 498 Collections.sort(sortedTypes); 499 fail("Policy exempts vendor domains from ban on executing files in /system: " 500 + sortedTypes); 501 } 502 } 503 504 /** 505 * Tests that mlstrustedsubject does not include untrusted_app 506 * and that mlstrustedobject does not include app_data_file. 507 * This helps prevent circumventing the per-user isolation of 508 * normal apps via levelFrom=user. 509 * 510 * @throws Exception 511 */ 512 @CddTest(requirement="9.7") testMLSAttributes()513 public void testMLSAttributes() throws Exception { 514 assertNotInAttribute("mlstrustedsubject", "untrusted_app"); 515 assertNotInAttribute("mlstrustedobject", "app_data_file"); 516 } 517 518 /** 519 * Tests that the seapp_contexts file on the device is valid. 520 * 521 * @throws Exception 522 */ 523 @CddTest(requirement="9.7") testValidSeappContexts()524 public void testValidSeappContexts() throws Exception { 525 526 /* obtain seapp_contexts file from running device */ 527 devicePlatSeappFile = File.createTempFile("plat_seapp_contexts", ".tmp"); 528 devicePlatSeappFile.deleteOnExit(); 529 deviceNonplatSeappFile = File.createTempFile("nonplat_seapp_contexts", ".tmp"); 530 deviceNonplatSeappFile.deleteOnExit(); 531 if (mDevice.pullFile("/system/etc/selinux/plat_seapp_contexts", devicePlatSeappFile)) { 532 mDevice.pullFile("/vendor/etc/selinux/nonplat_seapp_contexts", deviceNonplatSeappFile); 533 }else { 534 mDevice.pullFile("/plat_seapp_contexts", devicePlatSeappFile); 535 mDevice.pullFile("/nonplat_seapp_contexts", deviceNonplatSeappFile); 536 } 537 538 /* retrieve the checkseapp executable from jar */ 539 checkSeapp = copyResourceToTempFile("/checkseapp"); 540 checkSeapp.setExecutable(true); 541 542 /* retrieve the AOSP seapp_neverallows file from jar */ 543 seappNeverAllowFile = copyResourceToTempFile("/plat_seapp_neverallows"); 544 545 /* run checkseapp on seapp_contexts */ 546 ProcessBuilder pb = new ProcessBuilder(checkSeapp.getAbsolutePath(), 547 "-p", devicePolicyFile.getAbsolutePath(), 548 seappNeverAllowFile.getAbsolutePath(), 549 devicePlatSeappFile.getAbsolutePath(), 550 deviceNonplatSeappFile.getAbsolutePath()); 551 pb.redirectOutput(ProcessBuilder.Redirect.PIPE); 552 pb.redirectErrorStream(true); 553 Process p = pb.start(); 554 p.waitFor(); 555 BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream())); 556 String line; 557 StringBuilder errorString = new StringBuilder(); 558 while ((line = result.readLine()) != null) { 559 errorString.append(line); 560 errorString.append("\n"); 561 } 562 assertTrue("The seapp_contexts file was invalid:\n" 563 + errorString, errorString.length() == 0); 564 } 565 566 /** 567 * Asserts that the actual file contents starts with the expected file 568 * contents. 569 * 570 * @param expectedFile 571 * The file with the expected contents. 572 * @param actualFile 573 * The actual file being checked. 574 */ assertFileStartsWith(File expectedFile, File actualFile)575 private void assertFileStartsWith(File expectedFile, File actualFile) throws Exception { 576 BufferedReader expectedReader = new BufferedReader(new FileReader(expectedFile.getAbsolutePath())); 577 BufferedReader actualReader = new BufferedReader(new FileReader(actualFile.getAbsolutePath())); 578 String expectedLine, actualLine; 579 while ((expectedLine = expectedReader.readLine()) != null) { 580 actualLine = actualReader.readLine(); 581 assertEquals("Lines do not match:", expectedLine, actualLine); 582 } 583 } 584 585 /** 586 * Tests that the seapp_contexts file on the device contains 587 * the standard AOSP entries. 588 * 589 * @throws Exception 590 */ 591 @CddTest(requirement="9.7") testAospSeappContexts()592 public void testAospSeappContexts() throws Exception { 593 594 /* obtain seapp_contexts file from running device */ 595 devicePlatSeappFile = File.createTempFile("seapp_contexts", ".tmp"); 596 devicePlatSeappFile.deleteOnExit(); 597 if (!mDevice.pullFile("/system/etc/selinux/plat_seapp_contexts", devicePlatSeappFile)) { 598 mDevice.pullFile("/plat_seapp_contexts", devicePlatSeappFile); 599 } 600 /* retrieve the AOSP seapp_contexts file from jar */ 601 aospSeappFile = copyResourceToTempFile("/plat_seapp_contexts"); 602 603 assertFileStartsWith(aospSeappFile, devicePlatSeappFile); 604 } 605 606 /** 607 * Tests that the file_contexts.bin file on the device contains 608 * the standard AOSP entries. 609 * 610 * @throws Exception 611 */ 612 @CddTest(requirement="9.7") testAospFileContexts()613 public void testAospFileContexts() throws Exception { 614 615 /* retrieve the checkfc executable from jar */ 616 checkFc = copyResourceToTempFile("/checkfc"); 617 checkFc.setExecutable(true); 618 619 /* retrieve the AOSP file_contexts file from jar */ 620 aospFcFile = copyResourceToTempFile("/plat_file_contexts"); 621 622 /* run checkfc -c plat_file_contexts file_contexts.bin */ 623 ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(), 624 "-c", aospFcFile.getAbsolutePath(), 625 devicePlatFcFile.getAbsolutePath()); 626 pb.redirectOutput(ProcessBuilder.Redirect.PIPE); 627 pb.redirectErrorStream(true); 628 Process p = pb.start(); 629 p.waitFor(); 630 BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream())); 631 String line = result.readLine(); 632 assertTrue("The file_contexts file did not include the AOSP entries:\n" 633 + line + "\n", 634 line.equals("equal") || line.equals("subset")); 635 } 636 637 /** 638 * Tests that the property_contexts file on the device contains 639 * the standard AOSP entries. 640 * 641 * @throws Exception 642 */ 643 @CddTest(requirement="9.7") testAospPropertyContexts()644 public void testAospPropertyContexts() throws Exception { 645 646 /* obtain property_contexts file from running device */ 647 devicePcFile = File.createTempFile("plat_property_contexts", ".tmp"); 648 devicePcFile.deleteOnExit(); 649 // plat_property_contexts may be either in /system/etc/sepolicy or in / 650 if (!mDevice.pullFile("/system/etc/selinux/plat_property_contexts", devicePcFile)) { 651 mDevice.pullFile("/plat_property_contexts", devicePcFile); 652 } 653 654 // Retrieve the AOSP property_contexts file from JAR. 655 // The location of this file in the JAR has nothing to do with the location of this file on 656 // Android devices. See build script of this CTS module. 657 aospPcFile = copyResourceToTempFile("/plat_property_contexts"); 658 659 assertFileStartsWith(aospPcFile, devicePcFile); 660 } 661 662 /** 663 * Tests that the service_contexts file on the device contains 664 * the standard AOSP entries. 665 * 666 * @throws Exception 667 */ 668 @CddTest(requirement="9.7") testAospServiceContexts()669 public void testAospServiceContexts() throws Exception { 670 671 /* obtain service_contexts file from running device */ 672 deviceSvcFile = File.createTempFile("service_contexts", ".tmp"); 673 deviceSvcFile.deleteOnExit(); 674 if (!mDevice.pullFile("/system/etc/selinux/plat_service_contexts", deviceSvcFile)) { 675 mDevice.pullFile("/plat_service_contexts", deviceSvcFile); 676 } 677 678 /* retrieve the AOSP service_contexts file from jar */ 679 aospSvcFile = copyResourceToTempFile("/plat_service_contexts"); 680 681 assertFileStartsWith(aospSvcFile, deviceSvcFile); 682 } 683 684 /** 685 * Tests that the file_contexts file(s) on the device is valid. 686 * 687 * @throws Exception 688 */ 689 @CddTest(requirement="9.7") testValidFileContexts()690 public void testValidFileContexts() throws Exception { 691 692 /* retrieve the checkfc executable from jar */ 693 checkFc = copyResourceToTempFile("/checkfc"); 694 checkFc.setExecutable(true); 695 696 /* combine plat and nonplat policies for testing */ 697 File combinedFcFile = File.createTempFile("combined_file_context", ".tmp"); 698 combinedFcFile.deleteOnExit(); 699 appendTo(combinedFcFile.getAbsolutePath(), devicePlatFcFile.getAbsolutePath()); 700 appendTo(combinedFcFile.getAbsolutePath(), deviceNonplatFcFile.getAbsolutePath()); 701 702 /* run checkfc sepolicy file_contexts */ 703 ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(), 704 devicePolicyFile.getAbsolutePath(), 705 combinedFcFile.getAbsolutePath()); 706 pb.redirectOutput(ProcessBuilder.Redirect.PIPE); 707 pb.redirectErrorStream(true); 708 Process p = pb.start(); 709 p.waitFor(); 710 BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream())); 711 String line; 712 StringBuilder errorString = new StringBuilder(); 713 while ((line = result.readLine()) != null) { 714 errorString.append(line); 715 errorString.append("\n"); 716 } 717 assertTrue("file_contexts was invalid:\n" 718 + errorString, errorString.length() == 0); 719 } 720 721 /** 722 * Tests that the property_contexts file on the device is valid. 723 * 724 * @throws Exception 725 */ 726 @CddTest(requirement="9.7") testValidPropertyContexts()727 public void testValidPropertyContexts() throws Exception { 728 729 /* retrieve the checkfc executable from jar */ 730 File propertyInfoChecker = copyResourceToTempFile("/property_info_checker"); 731 propertyInfoChecker.setExecutable(true); 732 733 /* obtain property_contexts file from running device */ 734 devicePcFile = File.createTempFile("property_contexts", ".tmp"); 735 devicePcFile.deleteOnExit(); 736 // plat_property_contexts may be either in /system/etc/sepolicy or in / 737 if (!mDevice.pullFile("/system/etc/selinux/plat_property_contexts", devicePcFile)) { 738 mDevice.pullFile("/plat_property_contexts", devicePcFile); 739 } 740 741 /* run property_info_checker on property_contexts */ 742 ProcessBuilder pb = new ProcessBuilder(propertyInfoChecker.getAbsolutePath(), 743 devicePolicyFile.getAbsolutePath(), 744 devicePcFile.getAbsolutePath()); 745 pb.redirectOutput(ProcessBuilder.Redirect.PIPE); 746 pb.redirectErrorStream(true); 747 Process p = pb.start(); 748 p.waitFor(); 749 BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream())); 750 String line; 751 StringBuilder errorString = new StringBuilder(); 752 while ((line = result.readLine()) != null) { 753 errorString.append(line); 754 errorString.append("\n"); 755 } 756 assertTrue("The property_contexts file was invalid:\n" 757 + errorString, errorString.length() == 0); 758 } 759 760 /** 761 * Tests that the service_contexts file on the device is valid. 762 * 763 * @throws Exception 764 */ 765 @CddTest(requirement="9.7") testValidServiceContexts()766 public void testValidServiceContexts() throws Exception { 767 768 /* retrieve the checkfc executable from jar */ 769 checkFc = copyResourceToTempFile("/checkfc"); 770 checkFc.setExecutable(true); 771 772 /* obtain service_contexts file from running device */ 773 deviceSvcFile = File.createTempFile("service_contexts", ".tmp"); 774 deviceSvcFile.deleteOnExit(); 775 mDevice.pullFile("/service_contexts", deviceSvcFile); 776 777 /* run checkfc -s on service_contexts */ 778 ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(), 779 "-s", devicePolicyFile.getAbsolutePath(), 780 deviceSvcFile.getAbsolutePath()); 781 pb.redirectOutput(ProcessBuilder.Redirect.PIPE); 782 pb.redirectErrorStream(true); 783 Process p = pb.start(); 784 p.waitFor(); 785 BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream())); 786 String line; 787 StringBuilder errorString = new StringBuilder(); 788 while ((line = result.readLine()) != null) { 789 errorString.append(line); 790 errorString.append("\n"); 791 } 792 assertTrue("The service_contexts file was invalid:\n" 793 + errorString, errorString.length() == 0); 794 } 795 isMac()796 public static boolean isMac() { 797 String os = System.getProperty("os.name").toLowerCase(); 798 return (os.startsWith("mac") || os.startsWith("darwin")); 799 } 800 setupLibraries()801 private void setupLibraries() throws Exception { 802 // The host side binary tests are host OS specific. Use Linux 803 // libraries on Linux and Mac libraries on Mac. 804 if (isMac()) { 805 libsepolwrap = copyResourceToTempFile("/libsepolwrap.dylib"); 806 libcpp = copyResourceToTempFile("/libc++.dylib"); 807 libcpp.renameTo(new File(System.getProperty("java.io.tmpdir") + "/libc++.dylib")); 808 } else { 809 libsepolwrap = copyResourceToTempFile("/libsepolwrap.so"); 810 libcpp = copyResourceToTempFile("/libc++.so"); 811 libcpp.renameTo(new File(System.getProperty("java.io.tmpdir") + "/libc++.so")); 812 } 813 libsepolwrap.deleteOnExit(); 814 libcpp.deleteOnExit(); 815 } 816 assertSepolicyTests(String test, String testExecutable)817 private void assertSepolicyTests(String test, String testExecutable) throws Exception { 818 setupLibraries(); 819 sepolicyTests = copyResourceToTempFile(testExecutable); 820 sepolicyTests.setExecutable(true); 821 ProcessBuilder pb = new ProcessBuilder( 822 sepolicyTests.getAbsolutePath(), 823 "-l", libsepolwrap.getAbsolutePath(), 824 "-f", devicePlatFcFile.getAbsolutePath(), 825 "-f", deviceNonplatFcFile.getAbsolutePath(), 826 "-p", devicePolicyFile.getAbsolutePath(), 827 "--test", test); 828 Map<String, String> env = pb.environment(); 829 if (isMac()) { 830 env.put("DYLD_LIBRARY_PATH", System.getProperty("java.io.tmpdir")); 831 } else { 832 env.put("LD_LIBRARY_PATH", System.getProperty("java.io.tmpdir")); 833 } 834 pb.redirectOutput(ProcessBuilder.Redirect.PIPE); 835 pb.redirectErrorStream(true); 836 Process p = pb.start(); 837 p.waitFor(); 838 BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream())); 839 String line; 840 StringBuilder errorString = new StringBuilder(); 841 while ((line = result.readLine()) != null) { 842 errorString.append(line); 843 errorString.append("\n"); 844 } 845 assertTrue(errorString.toString(), errorString.length() == 0); 846 } 847 848 /** 849 * Tests that all types on /data have the data_file_type attribute. 850 * 851 * @throws Exception 852 */ testDataTypeViolators()853 public void testDataTypeViolators() throws Exception { 854 assertSepolicyTests("TestDataTypeViolations", "/sepolicy_tests"); 855 } 856 857 /** 858 * Tests that all types in /proc have the proc_type attribute. 859 * 860 * @throws Exception 861 */ testProcTypeViolators()862 public void testProcTypeViolators() throws Exception { 863 assertSepolicyTests("TestProcTypeViolations", "/sepolicy_tests"); 864 } 865 866 /** 867 * Tests that all types in /sys have the sysfs_type attribute. 868 * 869 * @throws Exception 870 */ testSysfsTypeViolators()871 public void testSysfsTypeViolators() throws Exception { 872 assertSepolicyTests("TestSysfsTypeViolations", "/sepolicy_tests"); 873 } 874 875 /** 876 * Tests that all types on /vendor have the vendor_file_type attribute. 877 * 878 * @throws Exception 879 */ testVendorTypeViolators()880 public void testVendorTypeViolators() throws Exception { 881 assertSepolicyTests("TestVendorTypeViolations", "/sepolicy_tests"); 882 } 883 884 /** 885 * Tests that all domains with entrypoints on /system have the coredomain 886 * attribute, and that all domains with entrypoints on /vendor do not have the 887 * coredomain attribute. 888 * 889 * @throws Exception 890 */ testCoredomainViolators()891 public void testCoredomainViolators() throws Exception { 892 assertSepolicyTests("CoredomainViolations", "/treble_sepolicy_tests"); 893 } 894 895 /** 896 * Tests that the policy defines no booleans (runtime conditional policy). 897 * 898 * @throws Exception 899 */ 900 @CddTest(requirement="9.7") testNoBooleans()901 public void testNoBooleans() throws Exception { 902 903 /* run sepolicy-analyze booleans check on policy file */ 904 ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(), 905 devicePolicyFile.getAbsolutePath(), "booleans"); 906 pb.redirectOutput(ProcessBuilder.Redirect.PIPE); 907 pb.redirectErrorStream(true); 908 Process p = pb.start(); 909 p.waitFor(); 910 BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream())); 911 String line; 912 StringBuilder errorString = new StringBuilder(); 913 while ((line = result.readLine()) != null) { 914 errorString.append(line); 915 errorString.append("\n"); 916 } 917 assertTrue("The policy contained booleans:\n" 918 + errorString, errorString.length() == 0); 919 } 920 921 /** 922 * Tests that important domain labels are being appropriately applied. 923 */ 924 925 /** 926 * Asserts that no processes are running in a domain. 927 * 928 * @param domain 929 * The domain or SELinux context to check. 930 */ assertDomainEmpty(String domain)931 private void assertDomainEmpty(String domain) throws DeviceNotAvailableException { 932 List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain); 933 String msg = "Expected no processes in SELinux domain \"" + domain + "\"" 934 + " Found: \"" + procs + "\""; 935 assertNull(msg, procs); 936 } 937 938 /** 939 * Asserts that a domain exists and that only one, well defined, process is 940 * running in that domain. 941 * 942 * @param domain 943 * The domain or SELinux context to check. 944 * @param executable 945 * The path of the executable or application package name. 946 */ assertDomainOne(String domain, String executable)947 private void assertDomainOne(String domain, String executable) throws DeviceNotAvailableException { 948 List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain); 949 List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable); 950 String msg = "Expected 1 process in SELinux domain \"" + domain + "\"" 951 + " Found \"" + procs + "\""; 952 assertNotNull(msg, procs); 953 assertEquals(msg, 1, procs.size()); 954 955 msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\"" 956 + "Found: \"" + procs + "\""; 957 assertEquals(msg, executable, procs.get(0).procTitle); 958 959 msg = "Expected 1 process with executable \"" + executable + "\"" 960 + " Found \"" + procs + "\""; 961 assertNotNull(msg, exeProcs); 962 assertEquals(msg, 1, exeProcs.size()); 963 964 msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\"" 965 + "Found: \"" + procs + "\""; 966 assertEquals(msg, domain, exeProcs.get(0).label); 967 } 968 969 /** 970 * Asserts that a domain may exist. If a domain exists, the cardinality of 971 * the domain is verified to be 1 and that the correct process is running in 972 * that domain. 973 * 974 * @param domain 975 * The domain or SELinux context to check. 976 * @param executable 977 * The path of the executable or application package name. 978 */ assertDomainZeroOrOne(String domain, String executable)979 private void assertDomainZeroOrOne(String domain, String executable) 980 throws DeviceNotAvailableException { 981 List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain); 982 List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable); 983 984 if (procs != null) { 985 String msg = "Expected 1 process in SELinux domain \"" + domain + "\"" 986 + " Found: \"" + procs + "\""; 987 assertEquals(msg, 1, procs.size()); 988 989 msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\"" 990 + "Found: \"" + procs.get(0) + "\""; 991 assertEquals(msg, executable, procs.get(0).procTitle); 992 } 993 994 if (exeProcs != null) { 995 String msg = "Expected 1 process with executable \"" + executable + "\"" 996 + " Found: \"" + procs + "\""; 997 assertEquals(msg, 1, exeProcs.size()); 998 999 msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\"" 1000 + "Found: \"" + procs.get(0) + "\""; 1001 assertEquals(msg, domain, exeProcs.get(0).label); 1002 } 1003 } 1004 1005 /** 1006 * Asserts that a domain must exist, and that the cardinality is greater 1007 * than or equal to 1. 1008 * 1009 * @param domain 1010 * The domain or SELinux context to check. 1011 * @param executables 1012 * The path of the allowed executables or application package names. 1013 */ assertDomainN(String domain, String... executables)1014 private void assertDomainN(String domain, String... executables) 1015 throws DeviceNotAvailableException { 1016 List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain); 1017 String msg = "Expected 1 or more processes in SELinux domain but found none."; 1018 assertNotNull(msg, procs); 1019 1020 Set<String> execList = new HashSet<String>(Arrays.asList(executables)); 1021 1022 for (ProcessDetails p : procs) { 1023 msg = "Expected one of \"" + execList + "\" in SELinux domain \"" + domain + "\"" 1024 + " Found: \"" + p + "\""; 1025 assertTrue(msg, execList.contains(p.procTitle)); 1026 } 1027 1028 for (String exe : executables) { 1029 List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(exe); 1030 1031 if (exeProcs != null) { 1032 for (ProcessDetails p : exeProcs) { 1033 msg = "Expected executable \"" + exe + "\" in SELinux domain \"" 1034 + domain + "\"" + " Found: \"" + p + "\""; 1035 assertEquals(msg, domain, p.label); 1036 } 1037 } 1038 } 1039 } 1040 1041 /** 1042 * Asserts that a domain, if it exists, is only running the listed executables. 1043 * 1044 * @param domain 1045 * The domain or SELinux context to check. 1046 * @param executables 1047 * The path of the allowed executables or application package names. 1048 */ assertDomainHasExecutable(String domain, String... executables)1049 private void assertDomainHasExecutable(String domain, String... executables) 1050 throws DeviceNotAvailableException { 1051 List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain); 1052 1053 if (procs != null) { 1054 Set<String> execList = new HashSet<String>(Arrays.asList(executables)); 1055 1056 for (ProcessDetails p : procs) { 1057 String msg = "Expected one of \"" + execList + "\" in SELinux domain \"" 1058 + domain + "\"" + " Found: \"" + p + "\""; 1059 assertTrue(msg, execList.contains(p.procTitle)); 1060 } 1061 } 1062 } 1063 1064 /** 1065 * Asserts that an executable exists and is only running in the listed domains. 1066 * 1067 * @param executable 1068 * The path of the executable to check. 1069 * @param domains 1070 * The list of allowed domains. 1071 */ assertExecutableHasDomain(String executable, String... domains)1072 private void assertExecutableHasDomain(String executable, String... domains) 1073 throws DeviceNotAvailableException { 1074 List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable); 1075 Set<String> domainList = new HashSet<String>(Arrays.asList(domains)); 1076 1077 String msg = "Expected 1 or more processes for executable \"" + executable + "\"."; 1078 assertNotNull(msg, exeProcs); 1079 1080 for (ProcessDetails p : exeProcs) { 1081 msg = "Expected one of \"" + domainList + "\" for executable \"" + executable 1082 + "\"" + " Found: \"" + p.label + "\""; 1083 assertTrue(msg, domainList.contains(p.label)); 1084 } 1085 } 1086 1087 /* Init is always there */ 1088 @CddTest(requirement="9.7") testInitDomain()1089 public void testInitDomain() throws DeviceNotAvailableException { 1090 assertDomainHasExecutable("u:r:init:s0", "/init"); 1091 assertDomainHasExecutable("u:r:vendor_init:s0", "/init"); 1092 assertExecutableHasDomain("/init", "u:r:init:s0", "u:r:vendor_init:s0"); 1093 } 1094 1095 /* Ueventd is always there */ 1096 @CddTest(requirement="9.7") testUeventdDomain()1097 public void testUeventdDomain() throws DeviceNotAvailableException { 1098 assertDomainOne("u:r:ueventd:s0", "/sbin/ueventd"); 1099 } 1100 1101 /* healthd may or may not exist */ 1102 @CddTest(requirement="9.7") testHealthdDomain()1103 public void testHealthdDomain() throws DeviceNotAvailableException { 1104 assertDomainZeroOrOne("u:r:healthd:s0", "/system/bin/healthd"); 1105 } 1106 1107 /* Servicemanager is always there */ 1108 @CddTest(requirement="9.7") testServicemanagerDomain()1109 public void testServicemanagerDomain() throws DeviceNotAvailableException { 1110 assertDomainOne("u:r:servicemanager:s0", "/system/bin/servicemanager"); 1111 } 1112 1113 /* Vold is always there */ 1114 @CddTest(requirement="9.7") testVoldDomain()1115 public void testVoldDomain() throws DeviceNotAvailableException { 1116 assertDomainOne("u:r:vold:s0", "/system/bin/vold"); 1117 } 1118 1119 /* netd is always there */ 1120 @CddTest(requirement="9.7") testNetdDomain()1121 public void testNetdDomain() throws DeviceNotAvailableException { 1122 assertDomainN("u:r:netd:s0", "/system/bin/netd", "/system/bin/iptables-restore", "/system/bin/ip6tables-restore"); 1123 } 1124 1125 /* Surface flinger is always there */ 1126 @CddTest(requirement="9.7") testSurfaceflingerDomain()1127 public void testSurfaceflingerDomain() throws DeviceNotAvailableException { 1128 assertDomainOne("u:r:surfaceflinger:s0", "/system/bin/surfaceflinger"); 1129 } 1130 1131 /* Zygote is always running */ 1132 @CddTest(requirement="9.7") testZygoteDomain()1133 public void testZygoteDomain() throws DeviceNotAvailableException { 1134 assertDomainN("u:r:zygote:s0", "zygote", "zygote64"); 1135 } 1136 1137 /* Checks drmserver for devices that require it */ 1138 @CddTest(requirement="9.7") testDrmServerDomain()1139 public void testDrmServerDomain() throws DeviceNotAvailableException { 1140 assertDomainZeroOrOne("u:r:drmserver:s0", "/system/bin/drmserver"); 1141 } 1142 1143 /* Installd is always running */ 1144 @CddTest(requirement="9.7") testInstalldDomain()1145 public void testInstalldDomain() throws DeviceNotAvailableException { 1146 assertDomainOne("u:r:installd:s0", "/system/bin/installd"); 1147 } 1148 1149 /* keystore is always running */ 1150 @CddTest(requirement="9.7") testKeystoreDomain()1151 public void testKeystoreDomain() throws DeviceNotAvailableException { 1152 assertDomainOne("u:r:keystore:s0", "/system/bin/keystore"); 1153 } 1154 1155 /* System server better be running :-P */ 1156 @CddTest(requirement="9.7") testSystemServerDomain()1157 public void testSystemServerDomain() throws DeviceNotAvailableException { 1158 assertDomainOne("u:r:system_server:s0", "system_server"); 1159 } 1160 1161 /* Watchdogd may or may not be there */ 1162 @CddTest(requirement="9.7") testWatchdogdDomain()1163 public void testWatchdogdDomain() throws DeviceNotAvailableException { 1164 assertDomainZeroOrOne("u:r:watchdogd:s0", "/sbin/watchdogd"); 1165 } 1166 1167 /* logd may or may not be there */ 1168 @CddTest(requirement="9.7") testLogdDomain()1169 public void testLogdDomain() throws DeviceNotAvailableException { 1170 assertDomainZeroOrOne("u:r:logd:s0", "/system/bin/logd"); 1171 } 1172 1173 /* lmkd may or may not be there */ 1174 @CddTest(requirement="9.7") testLmkdDomain()1175 public void testLmkdDomain() throws DeviceNotAvailableException { 1176 assertDomainZeroOrOne("u:r:lmkd:s0", "/system/bin/lmkd"); 1177 } 1178 1179 /* Wifi may be off so cardinality of 0 or 1 is ok */ 1180 @CddTest(requirement="9.7") testWpaDomain()1181 public void testWpaDomain() throws DeviceNotAvailableException { 1182 assertDomainZeroOrOne("u:r:wpa:s0", "/system/bin/wpa_supplicant"); 1183 } 1184 1185 /* 1186 * Nothing should be running in this domain, cardinality test is all thats 1187 * needed 1188 */ 1189 @CddTest(requirement="9.7") testInitShellDomain()1190 public void testInitShellDomain() throws DeviceNotAvailableException { 1191 assertDomainEmpty("u:r:init_shell:s0"); 1192 } 1193 1194 /* 1195 * Nothing should be running in this domain, cardinality test is all thats 1196 * needed 1197 */ 1198 @CddTest(requirement="9.7") testRecoveryDomain()1199 public void testRecoveryDomain() throws DeviceNotAvailableException { 1200 assertDomainEmpty("u:r:recovery:s0"); 1201 } 1202 1203 /* 1204 * Nothing should be running in this domain, cardinality test is all thats 1205 * needed 1206 */ 1207 @CddTest(requirement="9.7") 1208 @RestrictedBuildTest testSuDomain()1209 public void testSuDomain() throws DeviceNotAvailableException { 1210 assertDomainEmpty("u:r:su:s0"); 1211 } 1212 1213 /* 1214 * All kthreads should be in kernel context. 1215 */ 1216 @CddTest(requirement="9.7") testKernelDomain()1217 public void testKernelDomain() throws DeviceNotAvailableException { 1218 String domain = "u:r:kernel:s0"; 1219 List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain); 1220 if (procs != null) { 1221 for (ProcessDetails p : procs) { 1222 assertTrue("Non Kernel thread \"" + p + "\" found!", p.isKernel()); 1223 } 1224 } 1225 } 1226 1227 private static class ProcessDetails { 1228 public String label; 1229 public String user; 1230 public int pid; 1231 public int ppid; 1232 public String procTitle; 1233 1234 private static HashMap<String, ArrayList<ProcessDetails>> procMap; 1235 private static HashMap<String, ArrayList<ProcessDetails>> exeMap; 1236 private static int kernelParentThreadpid = -1; 1237 ProcessDetails(String label, String user, int pid, int ppid, String procTitle)1238 ProcessDetails(String label, String user, int pid, int ppid, String procTitle) { 1239 this.label = label; 1240 this.user = user; 1241 this.pid = pid; 1242 this.ppid = ppid; 1243 this.procTitle = procTitle; 1244 } 1245 1246 @Override toString()1247 public String toString() { 1248 return "label: " + label 1249 + " user: " + user 1250 + " pid: " + pid 1251 + " ppid: " + ppid 1252 + " cmd: " + procTitle; 1253 } 1254 1255 createProcMap(ITestDevice tDevice)1256 private static void createProcMap(ITestDevice tDevice) throws DeviceNotAvailableException { 1257 1258 /* take the output of a ps -Z to do our analysis */ 1259 CollectingOutputReceiver psOut = new CollectingOutputReceiver(); 1260 // TODO: remove "toybox" below and just run "ps" 1261 tDevice.executeShellCommand("toybox ps -A -o label,user,pid,ppid,cmdline", psOut); 1262 String psOutString = psOut.getOutput(); 1263 Pattern p = Pattern.compile( 1264 "^([\\w_:]+)\\s+([\\w_]+)\\s+(\\d+)\\s+(\\d+)\\s+(\\p{Graph}+)(\\s\\p{Graph}+)*\\s*$", 1265 Pattern.MULTILINE); 1266 Matcher m = p.matcher(psOutString); 1267 procMap = new HashMap<String, ArrayList<ProcessDetails>>(); 1268 exeMap = new HashMap<String, ArrayList<ProcessDetails>>(); 1269 while(m.find()) { 1270 String domainLabel = m.group(1); 1271 String user = m.group(2); 1272 int pid = Integer.parseInt(m.group(3)); 1273 int ppid = Integer.parseInt(m.group(4)); 1274 String procTitle = m.group(5); 1275 ProcessDetails proc = new ProcessDetails(domainLabel, user, pid, ppid, procTitle); 1276 if (procMap.get(domainLabel) == null) { 1277 procMap.put(domainLabel, new ArrayList<ProcessDetails>()); 1278 } 1279 procMap.get(domainLabel).add(proc); 1280 if (procTitle.equals("[kthreadd]") && ppid == 0) { 1281 kernelParentThreadpid = pid; 1282 } 1283 if (exeMap.get(procTitle) == null) { 1284 exeMap.put(procTitle, new ArrayList<ProcessDetails>()); 1285 } 1286 exeMap.get(procTitle).add(proc); 1287 } 1288 } 1289 getProcMap(ITestDevice tDevice)1290 public static HashMap<String, ArrayList<ProcessDetails>> getProcMap(ITestDevice tDevice) 1291 throws DeviceNotAvailableException{ 1292 if (procMap == null) { 1293 createProcMap(tDevice); 1294 } 1295 return procMap; 1296 } 1297 getExeMap(ITestDevice tDevice)1298 public static HashMap<String, ArrayList<ProcessDetails>> getExeMap(ITestDevice tDevice) 1299 throws DeviceNotAvailableException{ 1300 if (exeMap == null) { 1301 createProcMap(tDevice); 1302 } 1303 return exeMap; 1304 } 1305 isKernel()1306 public boolean isKernel() { 1307 return (pid == kernelParentThreadpid || ppid == kernelParentThreadpid); 1308 } 1309 } 1310 } 1311