Merge "Host side test corrupting fs-verity protected file."
This commit is contained in:
committed by
Android (Google) Code Review
commit
ab183847f0
@@ -23,7 +23,10 @@ package {
|
||||
|
||||
java_test_host {
|
||||
name: "PackageManagerServiceHostTests",
|
||||
srcs: ["src/**/*.kt"],
|
||||
srcs: [
|
||||
"src/**/*.java",
|
||||
"src/**/*.kt",
|
||||
],
|
||||
libs: [
|
||||
"tradefed",
|
||||
"junit",
|
||||
@@ -34,11 +37,17 @@ java_test_host {
|
||||
"cts-host-utils",
|
||||
"frameworks-base-hostutils",
|
||||
"PackageManagerServiceHostTestsIntentVerifyUtils",
|
||||
"block_device_writer_jar",
|
||||
],
|
||||
test_suites: ["general-tests"],
|
||||
data: [
|
||||
":PackageManagerTestApex",
|
||||
":PackageManagerTestApexApp",
|
||||
":PackageManagerServiceServerTests",
|
||||
],
|
||||
data_device_bins_both: [
|
||||
"block_device_writer",
|
||||
"fsverity_multilib",
|
||||
],
|
||||
java_resources: [
|
||||
":PackageManagerTestOverlayActor",
|
||||
|
||||
@@ -27,6 +27,22 @@
|
||||
value="pm uninstall com.android.cts.install.lib.testapp.A" />
|
||||
</target_preparer>
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
|
||||
<option name="cleanup-apks" value="true" />
|
||||
<option name="install-arg" value="-t" />
|
||||
<option name="test-file-name" value="PackageManagerServiceServerTests.apk" />
|
||||
</target_preparer>
|
||||
|
||||
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
|
||||
<!-- The build system produces both 32 and 64 bit variants with bitness suffix. Let
|
||||
FilePusher find the filename with bitness and push to a remote name without bitness.
|
||||
-->
|
||||
<option name="append-bitness" value="true" />
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
|
||||
<option name="push" value="fsverity_multilib->/data/local/tmp/fsverity_multilib" />
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.tradefed.testtype.HostTest">
|
||||
<option name="jar" value="PackageManagerServiceHostTests.jar" />
|
||||
</test>
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 com.android.server.pm.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import com.android.blockdevicewriter.BlockDeviceWriter;
|
||||
import com.android.tradefed.device.ITestDevice;
|
||||
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
|
||||
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
|
||||
import com.android.tradefed.util.CommandResult;
|
||||
import com.android.tradefed.util.CommandStatus;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* atest com.android.server.pm.test.SettingsTest
|
||||
*/
|
||||
@RunWith(DeviceJUnit4ClassRunner.class)
|
||||
public class SettingsTest extends BaseHostJUnit4Test {
|
||||
private static final String DEVICE_TEST_PACKAGE_NAME =
|
||||
"com.android.server.pm.test.service.server";
|
||||
private static final String TEST_CLASS_NAME =
|
||||
"com.android.server.pm.PackageManagerSettingsDeviceTests";
|
||||
|
||||
private static final String FSVERITY_EXECUTABLE = "/data/local/tmp/fsverity_multilib";
|
||||
private static final String DAMAGING_EXECUTABLE = "/data/local/tmp/block_device_writer";
|
||||
|
||||
@Test
|
||||
public void testWriteCorruptHeaderBinaryXml() throws Exception {
|
||||
// Corrupt 1st page, this will trigger error in initial resolve* call.
|
||||
performTest("testWriteBinaryXmlSettings", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteCorruptHeaderTextXml() throws Exception {
|
||||
// Corrupt 1st page, this will trigger error in initial resolve* call.
|
||||
performTest("testWriteTextXmlSettings", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteCorruptDataBinaryXml() throws Exception {
|
||||
// Corrupt 2nd page, this will trigger error in the XML parser.
|
||||
performTest("testWriteBinaryXmlSettings", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteCorruptDataTextXml() throws Exception {
|
||||
// Corrupt 2nd page, this will trigger error in the XML parser.
|
||||
performTest("testWriteTextXmlSettings", 1);
|
||||
}
|
||||
|
||||
private void performTest(String writeTest, int pageToCorrupt) throws Exception {
|
||||
assertTrue(runDeviceTests(DEVICE_TEST_PACKAGE_NAME, TEST_CLASS_NAME,
|
||||
writeTest));
|
||||
|
||||
int userId = getDevice().getCurrentUser();
|
||||
File filesDir = new File(
|
||||
"/data/user/" + userId + "/" + DEVICE_TEST_PACKAGE_NAME + "/files");
|
||||
File packagesXml = new File(filesDir, "system/packages.xml");
|
||||
|
||||
// Make sure fs-verity is enabled.
|
||||
enableFsVerity(packagesXml.getAbsolutePath());
|
||||
|
||||
// Damage the file directly against the block device.
|
||||
BlockDeviceWriter.damageFileAgainstBlockDevice(getDevice(), packagesXml.getAbsolutePath(),
|
||||
pageToCorrupt * 4096 + 1);
|
||||
BlockDeviceWriter.dropCaches(getDevice());
|
||||
|
||||
assertTrue(runDeviceTests(DEVICE_TEST_PACKAGE_NAME, TEST_CLASS_NAME,
|
||||
"testReadSettings"));
|
||||
}
|
||||
|
||||
private void enableFsVerity(String path) throws Exception {
|
||||
ITestDevice device = getDevice();
|
||||
ArrayList<String> args = new ArrayList<>();
|
||||
args.add(FSVERITY_EXECUTABLE);
|
||||
args.add("enable");
|
||||
args.add(path);
|
||||
|
||||
String cmd = String.join(" ", args);
|
||||
CommandResult result = device.executeShellV2Command(cmd);
|
||||
assertEquals("`" + cmd + "` failed: " + result.getStderr(), CommandStatus.SUCCESS,
|
||||
result.getStatus());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 com.android.server.pm;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.PropertyInvalidatedCache;
|
||||
import android.os.Message;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.permission.persistence.RuntimePermissionsPersistence;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.pm.permission.LegacyPermissionDataProvider;
|
||||
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
|
||||
import com.android.server.testutils.TestHandler;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class PackageManagerSettingsDeviceTests {
|
||||
@Mock
|
||||
RuntimePermissionsPersistence mRuntimePermissionsPersistence;
|
||||
@Mock
|
||||
LegacyPermissionDataProvider mPermissionDataProvider;
|
||||
@Mock
|
||||
DomainVerificationManagerInternal mDomainVerificationManager;
|
||||
@Mock
|
||||
Computer mComputer;
|
||||
|
||||
final TestHandler mHandler = new TestHandler((@NonNull Message msg) -> {
|
||||
return true;
|
||||
});
|
||||
|
||||
@Before
|
||||
public void initializeMocks() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mDomainVerificationManager.generateNewId())
|
||||
.thenAnswer(invocation -> UUID.randomUUID());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
// Disable binder caches in this process.
|
||||
PropertyInvalidatedCache.disableForTestMode();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void createUserManagerServiceRef() throws ReflectiveOperationException {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync((Runnable) () -> {
|
||||
try {
|
||||
// unregister the user manager from the local service
|
||||
LocalServices.removeServiceForTest(UserManagerInternal.class);
|
||||
new UserManagerService(InstrumentationRegistry.getContext());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail("Could not create user manager service; " + e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Write valid packages.xml, compare with the reserve copy.
|
||||
@Test
|
||||
public void testWriteBinaryXmlSettings() throws Exception {
|
||||
// write out files and read
|
||||
PackageManagerSettingsTests.writeOldFiles();
|
||||
Settings settings = makeSettings();
|
||||
assertTrue(settings.readLPw(mComputer, PackageManagerSettingsTests.createFakeUsers()));
|
||||
|
||||
// write out
|
||||
settings.writeLPr(mComputer, /*sync=*/true);
|
||||
|
||||
File filesDir = InstrumentationRegistry.getContext().getFilesDir();
|
||||
File packageXml = new File(filesDir, "system/packages.xml");
|
||||
File packagesReserveCopyXml = new File(filesDir, "system/packages.xml.reservecopy");
|
||||
// Primary.
|
||||
assertTrue(packageXml.exists());
|
||||
// For the test, we need a file with at least 2 pages.
|
||||
assertThat(packageXml.length(), greaterThan(4096L));
|
||||
// Reserve copy.
|
||||
assertTrue(packagesReserveCopyXml.exists());
|
||||
// Temporary backup.
|
||||
assertFalse(new File(filesDir, "packages-backup.xml").exists());
|
||||
|
||||
// compare two copies, make sure they are the same
|
||||
assertTrue(Arrays.equals(Files.readAllBytes(Path.of(packageXml.getAbsolutePath())),
|
||||
Files.readAllBytes(Path.of(packagesReserveCopyXml.getAbsolutePath()))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteTextXmlSettings() throws Exception {
|
||||
testWriteBinaryXmlSettings();
|
||||
|
||||
PackageManagerSettingsTests.writePackagesXml("system/packages.xml");
|
||||
|
||||
File filesDir = InstrumentationRegistry.getContext().getFilesDir();
|
||||
File packageXml = new File(filesDir, "system/packages.xml");
|
||||
|
||||
assertTrue(packageXml.exists());
|
||||
// For the test, we need a file with at least 2 pages.
|
||||
assertThat(packageXml.length(), greaterThan(4096L));
|
||||
}
|
||||
|
||||
// Read settings, verify.
|
||||
@Test
|
||||
public void testReadSettings() throws Exception {
|
||||
// This test can be run separately. In this case packages.xml is missing.
|
||||
assumeTrue(new File(InstrumentationRegistry.getContext().getFilesDir(),
|
||||
"system/packages.xml").exists());
|
||||
Settings settings = makeSettings();
|
||||
assertTrue(settings.readLPw(mComputer, PackageManagerSettingsTests.createFakeUsers()));
|
||||
PackageManagerSettingsTests.verifyKeySetMetaData(settings);
|
||||
}
|
||||
|
||||
private Settings makeSettings() {
|
||||
return new Settings(InstrumentationRegistry.getContext().getFilesDir(),
|
||||
mRuntimePermissionsPersistence, mPermissionDataProvider,
|
||||
mDomainVerificationManager, mHandler,
|
||||
new PackageManagerTracedLock());
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,6 @@ import android.platform.test.annotations.Presubmit;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.AtomicFile;
|
||||
import android.util.Log;
|
||||
import android.util.LongSparseArray;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
@@ -108,7 +107,6 @@ import java.util.concurrent.CountDownLatch;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class PackageManagerSettingsTests {
|
||||
private static final String TAG = "PackageManagerSettingsTests";
|
||||
private static final String PACKAGE_NAME_1 = "com.android.app1";
|
||||
private static final String PACKAGE_NAME_2 = "com.android.app2";
|
||||
private static final String PACKAGE_NAME_3 = "com.android.app3";
|
||||
@@ -142,6 +140,25 @@ public class PackageManagerSettingsTests {
|
||||
PropertyInvalidatedCache.disableForTestMode();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void createUserManagerServiceRef() throws ReflectiveOperationException {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync((Runnable) () -> {
|
||||
try {
|
||||
// unregister the user manager from the local service
|
||||
LocalServices.removeServiceForTest(UserManagerInternal.class);
|
||||
new UserManagerService(InstrumentationRegistry.getContext());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail("Could not create user manager service; " + e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
deleteFolder(InstrumentationRegistry.getContext().getFilesDir());
|
||||
}
|
||||
|
||||
/** make sure our initialized KeySetManagerService metadata matches packages.xml */
|
||||
@Test
|
||||
public void testReadKeySetSettings() throws Exception {
|
||||
@@ -1612,13 +1629,13 @@ public class PackageManagerSettingsTests {
|
||||
UUID.randomUUID());
|
||||
}
|
||||
|
||||
private @NonNull List<UserInfo> createFakeUsers() {
|
||||
static @NonNull List<UserInfo> createFakeUsers() {
|
||||
ArrayList<UserInfo> users = new ArrayList<>();
|
||||
users.add(new UserInfo(UserHandle.USER_SYSTEM, "test user", UserInfo.FLAG_INITIALIZED));
|
||||
return users;
|
||||
}
|
||||
|
||||
private void writeFile(File file, byte[] data) {
|
||||
private static void writeFile(File file, byte[] data) {
|
||||
file.mkdirs();
|
||||
try {
|
||||
AtomicFile aFile = new AtomicFile(file);
|
||||
@@ -1626,7 +1643,7 @@ public class PackageManagerSettingsTests {
|
||||
fos.write(data);
|
||||
aFile.finishWrite(fos);
|
||||
} catch (IOException ioe) {
|
||||
Log.e(TAG, "Cannot write file " + file.getPath());
|
||||
throw new RuntimeException("Cannot write file " + file.getPath(), ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1640,7 +1657,7 @@ public class PackageManagerSettingsTests {
|
||||
).getBytes());
|
||||
}
|
||||
|
||||
private void writePackagesXml(String fileName) {
|
||||
static void writePackagesXml(String fileName) {
|
||||
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), fileName),
|
||||
("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
|
||||
+ "<packages>"
|
||||
@@ -1748,7 +1765,7 @@ public class PackageManagerSettingsTests {
|
||||
.getBytes());
|
||||
}
|
||||
|
||||
private void writeStoppedPackagesXml() {
|
||||
private static void writeStoppedPackagesXml() {
|
||||
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages-stopped.xml"),
|
||||
( "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
|
||||
+ "<stopped-packages>"
|
||||
@@ -1758,7 +1775,7 @@ public class PackageManagerSettingsTests {
|
||||
.getBytes());
|
||||
}
|
||||
|
||||
private void writePackagesList() {
|
||||
private static void writePackagesList() {
|
||||
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages.list"),
|
||||
( "com.android.app1 11000 0 /data/data/com.android.app1 seinfo1"
|
||||
+ "com.android.app2 11001 0 /data/data/com.android.app2 seinfo2"
|
||||
@@ -1766,7 +1783,7 @@ public class PackageManagerSettingsTests {
|
||||
.getBytes());
|
||||
}
|
||||
|
||||
private void deleteSystemFolder() {
|
||||
private static void deleteSystemFolder() {
|
||||
File systemFolder = new File(InstrumentationRegistry.getContext().getFilesDir(), "system");
|
||||
deleteFolder(systemFolder);
|
||||
}
|
||||
@@ -1781,7 +1798,7 @@ public class PackageManagerSettingsTests {
|
||||
folder.delete();
|
||||
}
|
||||
|
||||
private void writeOldFiles() {
|
||||
static void writeOldFiles() {
|
||||
deleteSystemFolder();
|
||||
writePackagesXml("system/packages.xml");
|
||||
writeStoppedPackagesXml();
|
||||
@@ -1795,25 +1812,6 @@ public class PackageManagerSettingsTests {
|
||||
writePackagesList();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void createUserManagerServiceRef() throws ReflectiveOperationException {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync((Runnable) () -> {
|
||||
try {
|
||||
// unregister the user manager from the local service
|
||||
LocalServices.removeServiceForTest(UserManagerInternal.class);
|
||||
new UserManagerService(InstrumentationRegistry.getContext());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail("Could not create user manager service; " + e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
deleteFolder(InstrumentationRegistry.getTargetContext().getFilesDir());
|
||||
}
|
||||
|
||||
private Settings makeSettings() {
|
||||
return new Settings(InstrumentationRegistry.getContext().getFilesDir(),
|
||||
mRuntimePermissionsPersistence, mPermissionDataProvider,
|
||||
@@ -1821,7 +1819,7 @@ public class PackageManagerSettingsTests {
|
||||
new PackageManagerTracedLock());
|
||||
}
|
||||
|
||||
private void verifyKeySetMetaData(Settings settings)
|
||||
static void verifyKeySetMetaData(Settings settings)
|
||||
throws ReflectiveOperationException, IllegalAccessException {
|
||||
WatchedArrayMap<String, PackageSetting> packages = settings.mPackages;
|
||||
KeySetManagerService ksms = settings.getKeySetManagerService();
|
||||
|
||||
Reference in New Issue
Block a user