Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions tests/HardeningTest/Android.bp
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
filegroup {
name: "HardeningTestUtils",
srcs: ["src/grapheneos/hardeningtest/TestUtils.java"],
}

java_test_host {
name: "HardeningTest",
srcs: [
Expand Down
35 changes: 5 additions & 30 deletions tests/HardeningTest/src/grapheneos/hardeningtest/SELinuxTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@

import java.util.function.Consumer;

import static org.junit.Assert.assertEquals;

@RunWith(DeviceJUnit4ClassRunner.class)
public class SELinuxTest extends BaseHostJUnit4Test {

Expand All @@ -36,29 +34,6 @@ private void runDeviceTest(String pkgName, String name) {
}
}

private void editGosPackageState(String pkgName, int[] addFlags, int[] clearFlags) {
try {
var device = getDevice();
var cmd = new StringBuilder("pm edit-gos-package-state " + pkgName + " " + device.getCurrentUser());
for (int flag : addFlags) {
cmd.append(" add-flag ").append(flag);
}
for (int flag : clearFlags) {
cmd.append(" clear-flag ").append(flag);
}
var edRes = device.executeShellV2Command(cmd.toString());
assertEquals(edRes.toString(), 0L, (long) edRes.getExitCode());
} catch (DeviceNotAvailableException e) {
throw new IllegalStateException(e);
}
}

private void setComplexFlagState(String pkgName, int flag, int nonDefaultFlag, boolean isSet) {
int[] addFlags = isSet ? new int[] { nonDefaultFlag, flag } : new int[] { nonDefaultFlag };
int[] clearFlags = isSet ? new int[0] : new int[] { flag };
editGosPackageState(pkgName, addFlags, clearFlags);
}

private void forEachPackage(Consumer<String> action) {
for (String pkg : new String[] {TEST_PACKAGE_SDK_27, TEST_PACKAGE_SDK_LATEST, TEST_PACKAGE_SDK_LATEST_PREINSTALLED}) {
if (pkg == TEST_PACKAGE_SDK_LATEST_PREINSTALLED) {
Expand Down Expand Up @@ -97,12 +72,12 @@ String testName(String suffix) {
public void testDynamicCodeLoadingRestricted() {
forEachPackage(pkg -> {
for (var t : DclTestType.values()) {
setComplexFlagState(pkg, t.gosPsFlag, t.gosPsNonDefaultFlag, true);
TestUtils.setComplexFlagState(this, pkg, t.gosPsFlag, t.gosPsNonDefaultFlag, true);
runDeviceTest(pkg, t.testName("DclRestricted"));

if (pkg == TEST_PACKAGE_SDK_LATEST_PREINSTALLED) {
// check that DCL is blocked regardless of GosPackageState flags
setComplexFlagState(pkg, t.gosPsFlag, t.gosPsNonDefaultFlag, false);
TestUtils.setComplexFlagState(this, pkg, t.gosPsFlag, t.gosPsNonDefaultFlag, false);
runDeviceTest(pkg, t.testName("DclRestricted"));
}
}
Expand All @@ -118,7 +93,7 @@ public void testDynamicCodeLoadingAllowed() {
}

for (var t : DclTestType.values()) {
setComplexFlagState(pkg, t.gosPsFlag, t.gosPsNonDefaultFlag, false);
TestUtils.setComplexFlagState(this, pkg, t.gosPsFlag, t.gosPsNonDefaultFlag, false);
runDeviceTest(pkg, t.testName("DclAllowed"));
}
});
Expand All @@ -132,7 +107,7 @@ public void testPtraceAllowed() {
return;
}

setComplexFlagState(pkg,
TestUtils.setComplexFlagState(this, pkg,
GosPackageStateFlag.BLOCK_NATIVE_DEBUGGING,
GosPackageStateFlag.BLOCK_NATIVE_DEBUGGING_NON_DEFAULT,
false);
Expand All @@ -143,7 +118,7 @@ public void testPtraceAllowed() {
@Test
public void testPtraceDenied() {
forEachPackage(pkg -> {
setComplexFlagState(pkg,
TestUtils.setComplexFlagState(this, pkg,
GosPackageStateFlag.BLOCK_NATIVE_DEBUGGING,
GosPackageStateFlag.BLOCK_NATIVE_DEBUGGING_NON_DEFAULT,
true);
Expand Down
37 changes: 37 additions & 0 deletions tests/HardeningTest/src/grapheneos/hardeningtest/TestUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package grapheneos.hardeningtest;

import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;

import static org.junit.Assert.assertEquals;

public class TestUtils {
private TestUtils() {}

public static void editGosPackageState(BaseHostJUnit4Test host, String pkgName,
int[] addFlags, int[] clearFlags) {
try {
var device = host.getDevice();
var cmd = new StringBuilder("pm edit-gos-package-state " + pkgName
+ " " + device.getCurrentUser());
for (int flag : addFlags) {
cmd.append(" add-flag ").append(flag);
}
for (int flag : clearFlags) {
cmd.append(" clear-flag ").append(flag);
}
var edRes = device.executeShellV2Command(cmd.toString());
assertEquals(edRes.toString(), 0L, (long) edRes.getExitCode());
} catch (DeviceNotAvailableException e) {
throw new IllegalStateException(e);
}
}

public static void setComplexFlagState(BaseHostJUnit4Test host, String pkgName,
int flag, int nonDefaultFlag, boolean isSet) {
int[] addFlags = isSet ? new int[] { nonDefaultFlag, flag } : new int[] { nonDefaultFlag };
int[] clearFlags = isSet ? new int[0] : new int[] { flag };
editGosPackageState(host, pkgName, addFlags, clearFlags);
}
}
27 changes: 27 additions & 0 deletions tests/VaSpaceTest/Android.bp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
java_test_host {
name: "VaSpaceTest",
srcs: [
"src/**/*.java",
":GosPackageStateFlags",
":HardeningTestUtils",
],

libs: [
"tradefed",
"compatibility-tradefed",
"compatibility-host-util",
],

static_libs: [
"framework-annotations-lib",
"frameworks-base-hostutils",
],

test_suites: [
"general-tests",
],

device_common_data: [
":VaSpaceTestApp",
],
}
13 changes: 13 additions & 0 deletions tests/VaSpaceTest/AndroidTest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration description="Test of per-app extended VA space">

<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="VaSpaceTestApp.apk" />
</target_preparer>

<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="VaSpaceTest.jar" />
</test>

</configuration>
16 changes: 16 additions & 0 deletions tests/VaSpaceTest/VaSpaceTestApp/Android.bp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
android_test_helper_app {
name: "VaSpaceTestApp",

srcs: [
"src/**/*.kt",
],

platform_apis: true,

optimize: { enabled: false },

static_libs: [
"androidx.test.core",
"androidx.test.rules",
],
}
13 changes: 13 additions & 0 deletions tests/VaSpaceTest/VaSpaceTestApp/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.grapheneos.vaspacetest">

<application>
<uses-library android:name="android.test.runner" />
</application>

<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="${applicationId}" />

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package app.grapheneos.vaspacetest

import androidx.test.runner.AndroidJUnit4
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import java.io.File

@RunWith(AndroidJUnit4::class)
class VaSpaceDeviceTest {

@Test
fun testExtendedVaSpaceEnabled() = assertExtendedVaSpace(enabled = true)

@Test
fun testExtendedVaSpaceDisabled() = assertExtendedVaSpace(enabled = false)

private fun assertExtendedVaSpace(enabled: Boolean) {
// 39-bit VA limit: 2^39 = 0x80_0000_0000
val limit = 1L shl 39
val maps = File("/proc/self/maps").readLines()
if (enabled) {
val hasHighMapping = maps.any { line ->
line.substringBefore(' ').substringAfter('-').toLong(16) > limit
}
Assert.assertTrue("no mapping above 39-bit; extended VA space may not be active", hasHighMapping)
} else {
for (line in maps) {
val end = line.substringBefore(' ').substringAfter('-')
Assert.assertTrue(
"mapping end 0x$end exceeds 39-bit VA limit (0x${limit.toString(16)})",
end.toLong(16) <= limit,
)
}
}
}
}
54 changes: 54 additions & 0 deletions tests/VaSpaceTest/src/grapheneos/vaspacetest/VaSpaceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package grapheneos.vaspacetest;

import android.content.pm.GosPackageStateFlag;

import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;

import grapheneos.hardeningtest.TestUtils;

import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(DeviceJUnit4ClassRunner.class)
public class VaSpaceTest extends BaseHostJUnit4Test {

private static final String TEST_PACKAGE = "app.grapheneos.vaspacetest";
private static final String DEVICE_TEST_CLASS = TEST_PACKAGE + ".VaSpaceDeviceTest";

private void runDeviceTest(String methodName) {
var opts = new DeviceTestRunOptions(TEST_PACKAGE);
opts.setTestClassName(DEVICE_TEST_CLASS);
opts.setTestMethodName(methodName);
try {
runDeviceTests(opts);
} catch (DeviceNotAvailableException e) {
throw new IllegalStateException(e);
}
}

@Test
public void testExtendedVaSpaceEnabled() {
TestUtils.setComplexFlagState(this, TEST_PACKAGE,
GosPackageStateFlag.USE_EXTENDED_VA_SPACE,
GosPackageStateFlag.USE_EXTENDED_VA_SPACE_NON_DEFAULT,
true);
runDeviceTest("testExtendedVaSpaceEnabled");
}

@Test
public void testExtendedVaSpaceDisabled() {
// hardened_malloc forces extended VA space on, disable for testing
TestUtils.setComplexFlagState(this, TEST_PACKAGE,
GosPackageStateFlag.USE_HARDENED_MALLOC,
GosPackageStateFlag.USE_HARDENED_MALLOC_NON_DEFAULT,
false);
TestUtils.setComplexFlagState(this, TEST_PACKAGE,
GosPackageStateFlag.USE_EXTENDED_VA_SPACE,
GosPackageStateFlag.USE_EXTENDED_VA_SPACE_NON_DEFAULT,
false);
runDeviceTest("testExtendedVaSpaceDisabled");
}
}