Merge changes Ie725df5a,I18786a33
* changes:
pvmfw: Add top-level Error reporting
vmbase: Make BASE_ADDRESS public
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
index 46b9e77..0eb1257 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
@@ -17,26 +17,15 @@
package com.android.virt.fs;
import static com.android.microdroid.test.host.CommandResultSubject.assertThat;
-import static com.android.microdroid.test.host.LogArchiver.archiveLogThenDelete;
-import static com.android.tradefed.device.TestDevice.MicrodroidBuilder;
-import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
import android.platform.test.annotations.RootPermissionTest;
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.util.PollingCheck;
import com.android.microdroid.test.host.CommandRunner;
-import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.TestDevice;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -45,19 +34,10 @@
import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
import com.android.tradefed.util.CommandResult;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TestName;
import org.junit.runner.RunWith;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicBoolean;
-
@RootPermissionTest
@RunWith(DeviceJUnit4ClassRunner.class)
public final class AuthFsHostTest extends BaseHostJUnit4Test {
@@ -68,39 +48,15 @@
/** Output directory where the test can generate output on Android */
private static final String TEST_OUTPUT_DIR = "/data/local/tmp/authfs/output_dir";
- /** VM's log file */
- private static final String LOG_PATH = TEST_OUTPUT_DIR + "/log.txt";
-
- /** File name of the test APK */
- private static final String TEST_APK_NAME = "MicrodroidTestApp.apk";
-
- /** VM config entry path in the test APK */
- private static final String VM_CONFIG_PATH_IN_APK = "assets/vm_config.json";
-
- /** Path to open_then_run on Android */
- private static final String OPEN_THEN_RUN_BIN = "/data/local/tmp/open_then_run";
-
/** Path to fsverity on Android */
private static final String FSVERITY_BIN = "/data/local/tmp/fsverity";
/** Mount point of authfs on Microdroid during the test */
private static final String MOUNT_DIR = "/data/local/tmp";
- /** Path to fd_server on Android */
- private static final String FD_SERVER_BIN = "/apex/com.android.virt/bin/fd_server";
-
- /** Path to authfs on Microdroid */
- private static final String AUTHFS_BIN = "/system/bin/authfs";
-
/** Input manifest path in the VM. */
private static final String INPUT_MANIFEST_PATH = "/mnt/apk/assets/input_manifest.pb";
- /** Plenty of time for authfs to get ready */
- private static final int AUTHFS_INIT_TIMEOUT_MS = 3000;
-
- /** FUSE's magic from statfs(2) */
- private static final String FUSE_SUPER_MAGIC_HEX = "65735546";
-
// fs-verity digest (sha256) of testdata/input.{4k, 4k1, 4m}
private static final String DIGEST_4K =
"sha256-9828cd65f4744d6adda216d3a63d8205375be485bfa261b3b8153d3358f5a576";
@@ -113,86 +69,20 @@
private static CommandRunner sAndroid;
private static CommandRunner sMicrodroid;
- private static boolean sAssumptionFailed;
- private ExecutorService mThreadPool = Executors.newCachedThreadPool();
-
- @Rule public TestLogData mTestLogs = new TestLogData();
- @Rule public TestName mTestName = new TestName();
+ @Rule public final AuthFsTestRule mAuthFsTestRule = new AuthFsTestRule();
@BeforeClassWithInfo
public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
- assertNotNull(testInfo.getDevice());
- if (!(testInfo.getDevice() instanceof TestDevice)) {
- CLog.w("Unexpected type of ITestDevice. Skipping.");
- return;
- }
- TestDevice androidDevice = (TestDevice) testInfo.getDevice();
- sAndroid = new CommandRunner(androidDevice);
-
- // NB: We can't use assumeTrue because the assumption exception is NOT handled by the test
- // infra when it is thrown from a class method (see b/37502066). We need to skip both here
- // and in setUp.
- if (!androidDevice.supportsMicrodroid()) {
- CLog.i("Microdroid not supported. Skipping.");
- return;
- }
-
- // For each test case, boot and adb connect to a new Microdroid
- CLog.i("Starting the shared VM");
- ITestDevice microdroidDevice =
- MicrodroidBuilder
- .fromFile(findTestApk(testInfo.getBuildInfo()), VM_CONFIG_PATH_IN_APK)
- .debugLevel("full")
- .build((TestDevice) androidDevice);
-
- // From this point on, we need to tear down the Microdroid instance
- sMicrodroid = new CommandRunner(microdroidDevice);
-
- // Root because authfs (started from shell in this test) currently require root to open
- // /dev/fuse and mount the FUSE.
- assertThat(microdroidDevice.enableAdbRoot()).isTrue();
+ AuthFsTestRule.setUpClass(testInfo);
+ sAndroid = AuthFsTestRule.getAndroid();
+ sMicrodroid = AuthFsTestRule.getMicrodroid();
}
@AfterClassWithInfo
public static void afterClassWithDevice(TestInformation testInfo)
throws DeviceNotAvailableException {
- assertNotNull(sAndroid);
-
- if (sMicrodroid != null) {
- CLog.i("Shutting down shared VM");
- ((TestDevice) testInfo.getDevice()).shutdownMicrodroid(sMicrodroid.getDevice());
- sMicrodroid = null;
- }
-
- sAndroid = null;
- }
-
- @Before
- public void setUp() throws Exception {
- assumeTrue(((TestDevice) getDevice()).supportsMicrodroid());
- sAndroid.run("mkdir " + TEST_OUTPUT_DIR);
- }
-
- @After
- public void tearDown() throws Exception {
- if (sMicrodroid != null) {
- sMicrodroid.tryRun("killall authfs");
- sMicrodroid.tryRun("umount " + MOUNT_DIR);
- }
-
- assertNotNull(sAndroid);
- sAndroid.tryRun("killall fd_server");
-
- // Even though we only run one VM for the whole class, and could have collect the VM log
- // after all tests are done, TestLogData doesn't seem to work at class level. Hence,
- // collect recent logs manually for each test method.
- String vmRecentLog = TEST_OUTPUT_DIR + "/vm_recent.log";
- sAndroid.tryRun("tail -n 50 " + LOG_PATH + " > " + vmRecentLog);
- archiveLogThenDelete(mTestLogs, getDevice(), vmRecentLog,
- "vm_recent.log-" + mTestName.getMethodName());
-
- sAndroid.run("rm -rf " + TEST_OUTPUT_DIR);
+ AuthFsTestRule.tearDownClass(testInfo);
}
@Test
@@ -534,7 +424,7 @@
sAndroid.run("test ! -d " + androidOutputDir + "/dir/dir2");
// Can only delete a directory if empty
assertThat(sMicrodroid.runForResult("rmdir " + authfsOutputDir + "/dir")).isFailed();
- sMicrodroid.run("test -d " + authfsOutputDir + "/dir"); // still there
+ sMicrodroid.run("test -d " + authfsOutputDir + "/dir"); // still there
sMicrodroid.run("rm " + authfsOutputDir + "/dir/file");
sMicrodroid.run("rmdir " + authfsOutputDir + "/dir");
sMicrodroid.run("test ! -d " + authfsOutputDir + "/dir");
@@ -560,9 +450,9 @@
assertThat(sMicrodroid.runForResult("mkdir " + authfsOutputDir + "/some_file")).isFailed();
assertThat(sMicrodroid.runForResult("mkdir " + authfsOutputDir + "/some_dir")).isFailed();
assertThat(sMicrodroid.runForResult("mkdir " + authfsOutputDir + "/some_dir/file"))
- .isFailed();
+ .isFailed();
assertThat(sMicrodroid.runForResult("mkdir " + authfsOutputDir + "/some_dir/dir"))
- .isFailed();
+ .isFailed();
}
@Test
@@ -741,16 +631,8 @@
// Verify
// Magic matches. Has only 2 inodes (root and "/3").
assertEquals(
- FUSE_SUPER_MAGIC_HEX + " 2", sMicrodroid.run("stat -f -c '%t %c' " + MOUNT_DIR));
- }
-
- private static File findTestApk(IBuildInfo buildInfo) {
- try {
- return (new CompatibilityBuildHelper(buildInfo)).getTestFile(TEST_APK_NAME);
- } catch (FileNotFoundException e) {
- fail("Missing test file: " + TEST_APK_NAME);
- return null;
- }
+ mAuthFsTestRule.FUSE_SUPER_MAGIC_HEX + " 2",
+ sMicrodroid.run("stat -f -c '%t %c' " + MOUNT_DIR));
}
private void expectBackingFileConsistency(
@@ -832,66 +714,11 @@
}
private void runAuthFsOnMicrodroid(String flags) {
- String cmd = AUTHFS_BIN + " " + MOUNT_DIR + " " + flags;
-
- AtomicBoolean starting = new AtomicBoolean(true);
- mThreadPool.submit(
- () -> {
- // authfs may fail to start if fd_server is not yet listening on the vsock
- // ("Error: Invalid raw AIBinder"). Just restart if that happens.
- while (starting.get()) {
- try {
- CLog.i("Starting authfs");
- CommandResult result = sMicrodroid.runForResult(cmd);
- CLog.w("authfs has stopped: " + result);
- } catch (DeviceNotAvailableException e) {
- CLog.e("Error running authfs", e);
- throw new RuntimeException(e);
- }
- }
- });
- try {
- PollingCheck.waitFor(
- AUTHFS_INIT_TIMEOUT_MS, () -> isMicrodroidDirectoryOnFuse(MOUNT_DIR));
- } catch (Exception e) {
- // Convert the broad Exception into an unchecked exception to avoid polluting all other
- // methods. waitFor throws Exception because the callback, Callable#call(), has a
- // signature to throw an Exception.
- throw new RuntimeException(e);
- } finally {
- starting.set(false);
- }
+ mAuthFsTestRule.runAuthFsOnMicrodroid(flags);
}
private void runFdServerOnAndroid(String helperFlags, String fdServerFlags)
throws DeviceNotAvailableException {
- String cmd =
- "cd "
- + TEST_DIR
- + " && "
- + OPEN_THEN_RUN_BIN
- + " "
- + helperFlags
- + " -- "
- + FD_SERVER_BIN
- + " "
- + fdServerFlags;
-
- mThreadPool.submit(
- () -> {
- try {
- CLog.i("Starting fd_server");
- CommandResult result = sAndroid.runForResult(cmd);
- CLog.w("fd_server has stopped: " + result);
- } catch (DeviceNotAvailableException e) {
- CLog.e("Error running fd_server", e);
- throw new RuntimeException(e);
- }
- });
- }
-
- private boolean isMicrodroidDirectoryOnFuse(String path) throws DeviceNotAvailableException {
- String fs_type = sMicrodroid.tryRun("stat -f -c '%t' " + path);
- return FUSE_SUPER_MAGIC_HEX.equals(fs_type);
+ mAuthFsTestRule.runFdServerOnAndroid(helperFlags, fdServerFlags);
}
}
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java b/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java
new file mode 100644
index 0000000..e6081f7
--- /dev/null
+++ b/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2022 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.virt.fs;
+
+import static com.android.microdroid.test.host.LogArchiver.archiveLogThenDelete;
+import static com.android.tradefed.device.TestDevice.MicrodroidBuilder;
+import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.microdroid.test.host.CommandRunner;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.TestDevice;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.CommandResult;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** Custom TestRule for AuthFs tests. */
+public class AuthFsTestRule extends TestLogData {
+ /** FUSE's magic from statfs(2) */
+ static final String FUSE_SUPER_MAGIC_HEX = "65735546";
+
+ /** VM config entry path in the test APK */
+ private static final String VM_CONFIG_PATH_IN_APK = "assets/vm_config.json";
+
+ /** Test directory on Android where data are located */
+ private static final String TEST_DIR = "/data/local/tmp/authfs";
+
+ /** File name of the test APK */
+ private static final String TEST_APK_NAME = "MicrodroidTestApp.apk";
+
+ /** Output directory where the test can generate output on Android */
+ private static final String TEST_OUTPUT_DIR = "/data/local/tmp/authfs/output_dir";
+
+ /** Mount point of authfs on Microdroid during the test */
+ private static final String MOUNT_DIR = "/data/local/tmp";
+
+ /** VM's log file */
+ private static final String LOG_PATH = TEST_OUTPUT_DIR + "/log.txt";
+
+ /** Path to open_then_run on Android */
+ private static final String OPEN_THEN_RUN_BIN = "/data/local/tmp/open_then_run";
+
+ /** Path to fd_server on Android */
+ private static final String FD_SERVER_BIN = "/apex/com.android.virt/bin/fd_server";
+
+ /** Path to authfs on Microdroid */
+ private static final String AUTHFS_BIN = "/system/bin/authfs";
+
+ /** Plenty of time for authfs to get ready */
+ private static final int AUTHFS_INIT_TIMEOUT_MS = 3000;
+
+ private static TestInformation sTestInfo;
+ private static CommandRunner sAndroid;
+ private static CommandRunner sMicrodroid;
+
+ private final ExecutorService mThreadPool = Executors.newCachedThreadPool();
+
+ static void setUpClass(TestInformation testInfo) throws Exception {
+ assertNotNull(testInfo.getDevice());
+ if (!(testInfo.getDevice() instanceof TestDevice)) {
+ CLog.w("Unexpected type of ITestDevice. Skipping.");
+ return;
+ }
+ sTestInfo = testInfo;
+ TestDevice androidDevice = getDevice();
+ sAndroid = new CommandRunner(androidDevice);
+
+ // NB: We can't use assumeTrue because the assumption exception is NOT handled by the test
+ // infra when it is thrown from a class method (see b/37502066). We need to skip both here
+ // and in setUp.
+ if (!androidDevice.supportsMicrodroid()) {
+ CLog.i("Microdroid not supported. Skipping.");
+ return;
+ }
+
+ // For each test case, boot and adb connect to a new Microdroid
+ CLog.i("Starting the shared VM");
+ ITestDevice microdroidDevice =
+ MicrodroidBuilder.fromFile(
+ findTestApk(testInfo.getBuildInfo()), VM_CONFIG_PATH_IN_APK)
+ .debugLevel("full")
+ .build((TestDevice) androidDevice);
+
+ // From this point on, we need to tear down the Microdroid instance
+ sMicrodroid = new CommandRunner(microdroidDevice);
+
+ // Root because authfs (started from shell in this test) currently require root to open
+ // /dev/fuse and mount the FUSE.
+ assertThat(microdroidDevice.enableAdbRoot()).isTrue();
+ }
+
+ static void tearDownClass(TestInformation testInfo) throws DeviceNotAvailableException {
+ assertNotNull(sAndroid);
+
+ if (sMicrodroid != null) {
+ CLog.i("Shutting down shared VM");
+ ((TestDevice) testInfo.getDevice()).shutdownMicrodroid(sMicrodroid.getDevice());
+ sMicrodroid = null;
+ }
+
+ sAndroid = null;
+ }
+
+ /** This method is supposed to be called after {@link #setUpTest()}. */
+ static CommandRunner getAndroid() {
+ assertThat(sAndroid).isNotNull();
+ return sAndroid;
+ }
+
+ /** This method is supposed to be called after {@link #setUpTest()}. */
+ static CommandRunner getMicrodroid() {
+ assertThat(sMicrodroid).isNotNull();
+ return sMicrodroid;
+ }
+
+ @Override
+ public Statement apply(final Statement base, Description description) {
+ return super.apply(
+ new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ setUpTest();
+ base.evaluate();
+ tearDownTest(description.getMethodName());
+ }
+ },
+ description);
+ }
+
+ void runFdServerOnAndroid(String helperFlags, String fdServerFlags)
+ throws DeviceNotAvailableException {
+ String cmd =
+ "cd "
+ + TEST_DIR
+ + " && "
+ + OPEN_THEN_RUN_BIN
+ + " "
+ + helperFlags
+ + " -- "
+ + FD_SERVER_BIN
+ + " "
+ + fdServerFlags;
+ Future<?> unusedFuture = mThreadPool.submit(() -> runForResult(sAndroid, cmd, "fd_server"));
+ }
+
+ void runAuthFsOnMicrodroid(String flags) {
+ String cmd = AUTHFS_BIN + " " + MOUNT_DIR + " " + flags;
+
+ AtomicBoolean starting = new AtomicBoolean(true);
+ Future<?> unusedFuture =
+ mThreadPool.submit(
+ () -> {
+ // authfs may fail to start if fd_server is not yet listening on the
+ // vsock
+ // ("Error: Invalid raw AIBinder"). Just restart if that happens.
+ while (starting.get()) {
+ runForResult(sMicrodroid, cmd, "authfs");
+ }
+ });
+ try {
+ PollingCheck.waitFor(
+ AUTHFS_INIT_TIMEOUT_MS, () -> isMicrodroidDirectoryOnFuse(MOUNT_DIR));
+ } catch (Exception e) {
+ // Convert the broad Exception into an unchecked exception to avoid polluting all other
+ // methods. waitFor throws Exception because the callback, Callable#call(), has a
+ // signature to throw an Exception.
+ throw new RuntimeException(e);
+ } finally {
+ starting.set(false);
+ }
+ }
+
+ private static File findTestApk(IBuildInfo buildInfo) {
+ try {
+ return (new CompatibilityBuildHelper(buildInfo)).getTestFile(TEST_APK_NAME);
+ } catch (FileNotFoundException e) {
+ fail("Missing test file: " + TEST_APK_NAME);
+ return null;
+ }
+ }
+
+ private static TestDevice getDevice() {
+ return (TestDevice) sTestInfo.getDevice();
+ }
+
+ private void runForResult(CommandRunner cmdRunner, String cmd, String serviceName) {
+ try {
+ CLog.i("Starting " + serviceName);
+ CommandResult result = cmdRunner.runForResult(cmd);
+ CLog.w(serviceName + " has stopped: " + result);
+ } catch (DeviceNotAvailableException e) {
+ CLog.e("Error running " + serviceName, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private boolean isMicrodroidDirectoryOnFuse(String path) throws DeviceNotAvailableException {
+ String fs_type = sMicrodroid.tryRun("stat -f -c '%t' " + path);
+ return FUSE_SUPER_MAGIC_HEX.equals(fs_type);
+ }
+
+ private void setUpTest() throws Exception {
+ assumeTrue(getDevice().supportsMicrodroid());
+ sAndroid.run("mkdir -p " + TEST_OUTPUT_DIR);
+ }
+
+ private void tearDownTest(String testName) throws Exception {
+ if (sMicrodroid != null) {
+ sMicrodroid.tryRun("killall authfs");
+ sMicrodroid.tryRun("umount " + MOUNT_DIR);
+ }
+
+ assertNotNull(sAndroid);
+ sAndroid.tryRun("killall fd_server");
+
+ // Even though we only run one VM for the whole class, and could have collect the VM log
+ // after all tests are done, TestLogData doesn't seem to work at class level. Hence,
+ // collect recent logs manually for each test method.
+ String vmRecentLog = TEST_OUTPUT_DIR + "/vm_recent.log";
+ sAndroid.tryRun("tail -n 50 " + LOG_PATH + " > " + vmRecentLog);
+ archiveLogThenDelete(this, getDevice(), vmRecentLog, "vm_recent.log-" + testName);
+
+ sAndroid.run("rm -rf " + TEST_OUTPUT_DIR);
+ }
+}
diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md
index 7fddc5a..34a990d 100644
--- a/docs/getting_started/index.md
+++ b/docs/getting_started/index.md
@@ -118,7 +118,7 @@
on pVM. You can manually run the demo app on top of Microdroid as follows:
```shell
-TARGET_BUILD_APPS=MicrodroidDemoApp m apps_only dist
+UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true TARGET_BUILD_APPS=MicrodroidDemoApp m apps_only dist
adb shell mkdir -p /data/local/tmp/virt
adb push out/dist/MicrodroidDemoApp.apk /data/local/tmp/virt/
adb shell /apex/com.android.virt/bin/vm run-app \
diff --git a/microdroid/init.rc b/microdroid/init.rc
index 47002c9..8b2bbdb 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -177,7 +177,7 @@
mkdir /data/local/tmp 0771 shell shell
service tombstone_transmit /system/bin/tombstone_transmit.microdroid -cid 2 -port 2000 -remove_tombstones_after_transmitting
- user root
+ user system
group system
shutdown critical
@@ -186,12 +186,14 @@
group system
oneshot
disabled
+ capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER SYS_ADMIN
service ueventd /system/bin/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
+ capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID MKNOD NET_ADMIN SETGID SETUID SYS_MODULE SYS_RAWIO
service console /system/bin/sh
class core
diff --git a/microdroid/payload/metadata.proto b/microdroid/payload/metadata.proto
index 229d03f..06cbbf4 100644
--- a/microdroid/payload/metadata.proto
+++ b/microdroid/payload/metadata.proto
@@ -68,12 +68,8 @@
// Path to the payload binary file inside the APK.
string payload_binary_path = 1;
- // Required.
- // Whether tombstones from crashes inside the VM should be exported to the host.
- bool export_tombstones = 2;
-
// Optional.
// Arguments to be passed to the payload.
// TODO(b/249064104): Remove this
- repeated string args = 3;
+ repeated string args = 2;
}
diff --git a/microdroid/vm_payload/Android.bp b/microdroid/vm_payload/Android.bp
new file mode 100644
index 0000000..0424fc1
--- /dev/null
+++ b/microdroid/vm_payload/Android.bp
@@ -0,0 +1,19 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_ffi_static {
+ name: "libvm_payload",
+ crate_name: "vm_payload",
+ srcs: ["src/*.rs"],
+ include_dirs: ["include"],
+ prefer_rlib: true,
+ rustlibs: [
+ "android.system.virtualmachineservice-rust",
+ "libandroid_logger",
+ "libanyhow",
+ "libbinder_rs",
+ "liblog_rust",
+ "librpcbinder_rs",
+ ],
+}
diff --git a/microdroid/vm_payload/include/vm_payload.h b/microdroid/vm_payload/include/vm_payload.h
new file mode 100644
index 0000000..36480da
--- /dev/null
+++ b/microdroid/vm_payload/include/vm_payload.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Notifies the host that the payload is ready.
+/// Returns true if the notification succeeds else false.
+bool notify_payload_ready();
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/microdroid/vm_payload/src/lib.rs b/microdroid/vm_payload/src/lib.rs
new file mode 100644
index 0000000..394578a
--- /dev/null
+++ b/microdroid/vm_payload/src/lib.rs
@@ -0,0 +1,19 @@
+// Copyright 2022, 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.
+
+//! Library for payload to communicate with the VM service.
+
+mod vm_service;
+
+pub use vm_service::notify_payload_ready;
diff --git a/microdroid/vm_payload/src/vm_service.rs b/microdroid/vm_payload/src/vm_service.rs
new file mode 100644
index 0000000..e5a6b9a
--- /dev/null
+++ b/microdroid/vm_payload/src/vm_service.rs
@@ -0,0 +1,52 @@
+// Copyright 2022, 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.
+
+//! This module handles the interaction with virtual machine service.
+
+use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
+ VM_BINDER_SERVICE_PORT, IVirtualMachineService};
+use anyhow::{Context, Result};
+use binder::Strong;
+use log::{error, info, Level};
+use rpcbinder::get_vsock_rpc_interface;
+
+/// The CID representing the host VM
+const VMADDR_CID_HOST: u32 = 2;
+
+/// Notifies the host that the payload is ready.
+/// Returns true if the notification succeeds else false.
+#[no_mangle]
+pub extern "C" fn notify_payload_ready() -> bool {
+ android_logger::init_once(
+ android_logger::Config::default().with_tag("vm_payload").with_min_level(Level::Debug),
+ );
+ if let Err(e) = try_notify_payload_ready() {
+ error!("Failed to notify ready: {}", e);
+ false
+ } else {
+ info!("Notified host payload ready successfully");
+ true
+ }
+}
+
+/// Notifies the host that the payload is ready.
+/// Returns a `Result` containing error information if failed.
+fn try_notify_payload_ready() -> Result<()> {
+ get_vm_service()?.notifyPayloadReady().context("Cannot notify payload ready")
+}
+
+fn get_vm_service() -> Result<Strong<dyn IVirtualMachineService>> {
+ get_vsock_rpc_interface(VMADDR_CID_HOST, VM_BINDER_SERVICE_PORT as u32)
+ .context("Connecting to IVirtualMachineService")
+}
diff --git a/microdroid_manager/microdroid_manager.rc b/microdroid_manager/microdroid_manager.rc
index 60d8ab7..74a219d 100644
--- a/microdroid_manager/microdroid_manager.rc
+++ b/microdroid_manager/microdroid_manager.rc
@@ -4,3 +4,5 @@
setenv RUST_LOG info
# TODO(jooyung) remove this when microdroid_manager becomes a daemon
oneshot
+ # SYS_BOOT is required to exec kexecload from microdroid_manager
+ capabilities AUDIT_CONTROL SYS_ADMIN SYS_BOOT
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 90cabb6..b4b2c8b 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -30,12 +30,12 @@
use anyhow::{anyhow, bail, ensure, Context, Error, Result};
use apkverify::{get_public_key_der, verify, V4Signature};
use binder::{wait_for_interface, Strong};
-use diced_utils::cbor::encode_header;
+use diced_utils::cbor::{encode_header, encode_number};
use glob::glob;
use itertools::sorted;
use log::{error, info};
use microdroid_metadata::{write_metadata, Metadata, PayloadMetadata};
-use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
+use microdroid_payload_config::{Task, TaskType, VmPayloadConfig, OsConfig};
use openssl::sha::Sha512;
use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
use rand::Fill;
@@ -191,7 +191,10 @@
}
}
-fn dice_derivation(verified_data: &MicrodroidData, payload_config_path: &str) -> Result<()> {
+fn dice_derivation(
+ verified_data: &MicrodroidData,
+ payload_metadata: &PayloadMetadata,
+) -> Result<()> {
// Calculate compound digests of code and authorities
let mut code_hash_ctx = Sha512::new();
let mut authority_hash_ctx = Sha512::new();
@@ -210,15 +213,33 @@
// {
// -70002: "Microdroid payload",
- // -71000: payload_config_path
+ // ? -71000: tstr // payload_config_path
+ // ? -71001: PayloadConfig
// }
+ // PayloadConfig = {
+ // 1: tstr // payload_binary_path
+ // // TODO(b/249064104 Either include args, or deprecate them
+ // }
+
let mut config_desc = vec![
- 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
- 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
+ 0xa2, // map(2)
+ 0x3a, 0x00, 0x01, 0x11, 0x71, // -70002
+ 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79,
+ 0x6c, 0x6f, 0x61, 0x64, // "Microdroid payload"
];
- let config_path_bytes = payload_config_path.as_bytes();
- encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
- config_desc.extend_from_slice(config_path_bytes);
+
+ match payload_metadata {
+ PayloadMetadata::config_path(payload_config_path) => {
+ encode_negative_number(-71000, &mut config_desc)?;
+ encode_tstr(payload_config_path, &mut config_desc)?;
+ }
+ PayloadMetadata::config(payload_config) => {
+ encode_negative_number(-71001, &mut config_desc)?;
+ encode_header(5, 1, &mut config_desc)?; // map(1)
+ encode_number(1, &mut config_desc)?;
+ encode_tstr(&payload_config.payload_binary_path, &mut config_desc)?;
+ }
+ }
// Check app debuggability, conervatively assuming it is debuggable
let app_debuggable = system_properties::read_bool(APP_DEBUGGABLE_PROP, true)?;
@@ -240,6 +261,19 @@
Ok(())
}
+fn encode_tstr(tstr: &str, buffer: &mut Vec<u8>) -> Result<()> {
+ let bytes = tstr.as_bytes();
+ encode_header(3, bytes.len().try_into().unwrap(), buffer)?;
+ buffer.extend_from_slice(bytes);
+ Ok(())
+}
+
+fn encode_negative_number(n: i64, buffer: &mut dyn Write) -> Result<()> {
+ ensure!(n < 0);
+ let n = -1 - n;
+ encode_header(1, n.try_into().unwrap(), buffer)
+}
+
fn is_strict_boot() -> bool {
Path::new(AVF_STRICT_BOOT).exists()
}
@@ -301,14 +335,13 @@
verified_data
};
- let payload_config_path = match metadata.payload {
- Some(PayloadMetadata::config_path(p)) => p,
- _ => bail!("Unsupported payload config"),
- };
+ let payload_metadata = metadata.payload.ok_or_else(|| {
+ MicrodroidError::InvalidConfig("No payload config in metadata".to_string())
+ })?;
// To minimize the exposure to untrusted data, derive dice profile as soon as possible.
info!("DICE derivation for payload");
- dice_derivation(&verified_data, &payload_config_path)?;
+ dice_derivation(&verified_data, &payload_metadata)?;
// Before reading a file from the APK, start zipfuse
let noexec = false;
@@ -320,12 +353,7 @@
)
.context("Failed to run zipfuse")?;
- ensure!(
- !payload_config_path.is_empty(),
- MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
- );
-
- let config = load_config(Path::new(&payload_config_path))?;
+ let config = load_config(payload_metadata)?;
let task = config
.task
@@ -334,7 +362,7 @@
if config.extra_apks.len() != verified_data.extra_apks_data.len() {
return Err(anyhow!(
- "config expects {} extra apks, but found only {}",
+ "config expects {} extra apks, but found {}",
config.extra_apks.len(),
verified_data.extra_apks_data.len()
));
@@ -620,10 +648,31 @@
}
}
-fn load_config(path: &Path) -> Result<VmPayloadConfig> {
- info!("loading config from {:?}...", path);
- let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
- Ok(serde_json::from_reader(file)?)
+fn load_config(payload_metadata: PayloadMetadata) -> Result<VmPayloadConfig> {
+ match payload_metadata {
+ PayloadMetadata::config_path(path) => {
+ let path = Path::new(&path);
+ info!("loading config from {:?}...", path);
+ let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
+ Ok(serde_json::from_reader(file)?)
+ }
+ PayloadMetadata::config(payload_config) => {
+ let task = Task {
+ type_: TaskType::MicrodroidLauncher,
+ command: payload_config.payload_binary_path,
+ args: payload_config.args.into_vec(),
+ };
+ Ok(VmPayloadConfig {
+ os: OsConfig { name: "microdroid".to_owned() },
+ task: Some(task),
+ apexes: vec![],
+ extra_apks: vec![],
+ prefer_staged: false,
+ export_tombstones: false,
+ enable_authfs: false,
+ })
+ }
+ }
}
/// Loads the crashkernel into memory using kexec if the VM is loaded with `crashkernel=' parameter
diff --git a/tests/helper/src/java/com/android/microdroid/test/common/ProcessUtil.java b/tests/helper/src/java/com/android/microdroid/test/common/ProcessUtil.java
index 611a572..d85929d 100644
--- a/tests/helper/src/java/com/android/microdroid/test/common/ProcessUtil.java
+++ b/tests/helper/src/java/com/android/microdroid/test/common/ProcessUtil.java
@@ -36,14 +36,14 @@
public static List<SMapEntry> getProcessSmaps(int pid, Function<String, String> shellExecutor)
throws IOException {
String path = "/proc/" + pid + "/smaps";
- return parseSmaps(shellExecutor.apply("cat " + path + " || true"));
+ return parseMemoryInfo(shellExecutor.apply("cat " + path + " || true"));
}
/** Gets metrics key and values mapping of specified process id */
public static Map<String, Long> getProcessSmapsRollup(
int pid, Function<String, String> shellExecutor) throws IOException {
String path = "/proc/" + pid + "/smaps_rollup";
- List<SMapEntry> entries = parseSmaps(shellExecutor.apply("cat " + path + " || true"));
+ List<SMapEntry> entries = parseMemoryInfo(shellExecutor.apply("cat " + path + " || true"));
if (entries.size() > 1) {
throw new RuntimeException(
"expected at most one entry in smaps_rollup, got " + entries.size());
@@ -54,6 +54,21 @@
return new HashMap<String, Long>();
}
+ /** Gets global memory metrics key and values mapping */
+ public static Map<String, Long> getProcessMemoryMap(
+ Function<String, String> shellExecutor) throws IOException {
+ // The input file of parseMemoryInfo need a header string as the key of output entries.
+ // /proc/meminfo doesn't have this line so add one as the key.
+ String header = "device memory info\n";
+ List<SMapEntry> entries = parseMemoryInfo(header
+ + shellExecutor.apply("cat /proc/meminfo"));
+ if (entries.size() != 1) {
+ throw new RuntimeException(
+ "expected one entry in /proc/meminfo, got " + entries.size());
+ }
+ return entries.get(0).metrics;
+ }
+
/** Gets process id and process name mapping of the device */
public static Map<Integer, String> getProcessMap(Function<String, String> shellExecutor)
throws IOException {
@@ -77,7 +92,7 @@
// To ensures that only one object is created at a time.
private ProcessUtil() {}
- private static List<SMapEntry> parseSmaps(String file) {
+ private static List<SMapEntry> parseMemoryInfo(String file) {
List<SMapEntry> entries = new ArrayList<SMapEntry>();
for (String line : file.split("\n")) {
line = line.trim();
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
index 7a71254..9dd2a15 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
@@ -71,7 +71,6 @@
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -684,34 +683,6 @@
shutdownMicrodroid(getDevice(), cid);
}
- /**
- * TODO(b/249409434): to be replaced by ProcessUtil
- *
- * @deprecated use ProcessUtil instead.
- */
- @Deprecated
- private Map<String, Long> parseMemInfo(String file) {
- Map<String, Long> stats = new HashMap<>();
- file.lines().forEach(line -> {
- if (line.endsWith(" kB")) line = line.substring(0, line.length() - 3);
-
- String[] elems = line.split(":");
- assertThat(elems.length).isEqualTo(2);
- stats.put(elems[0].trim(), Long.parseLong(elems[1].trim()));
- });
- return stats;
- }
-
- /**
- * TODO(b/249409434): to be replaced by ProcessUtil
- *
- * @deprecated use ProcessUtil instead.
- */
- @Deprecated
- private Map<String, Long> getProcMemInfo() {
- return parseMemInfo(runOnMicrodroid("cat", "/proc/meminfo"));
- }
-
@Test
public void testMicrodroidRamUsage() throws Exception {
final String configPath = "assets/vm_config.json";
@@ -729,7 +700,8 @@
waitForBootComplete();
rootMicrodroid();
- for (Map.Entry<String, Long> stat : getProcMemInfo().entrySet()) {
+ for (Map.Entry<String, Long> stat :
+ ProcessUtil.getProcessMemoryMap(cmd -> runOnMicrodroid(cmd)).entrySet()) {
mMetrics.addTestMetric(
mMetricPrefix + "meminfo/" + stat.getKey().toLowerCase(),
stat.getValue().toString());
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 47116eb..9e6958c 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -33,7 +33,6 @@
srcs: ["src/native/testbinary.cpp"],
shared_libs: [
"android.security.dice-ndk",
- "android.system.virtualmachineservice-ndk",
"com.android.microdroid.testservice-ndk",
"libbase",
"libbinder_ndk",
@@ -44,6 +43,7 @@
"libfsverity_digests_proto_cc",
"liblog",
"libprotobuf-cpp-lite-ndk",
+ "libvm_payload",
],
}
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index b4fee86..fd8e776 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
#include <aidl/android/security/dice/IDiceNode.h>
-#include <aidl/android/system/virtualmachineservice/IVirtualMachineService.h>
#include <aidl/com/android/microdroid/testservice/BnTestService.h>
#include <android-base/file.h>
#include <android-base/properties.h>
@@ -33,11 +32,11 @@
#include <binder_rpc_unstable.hpp>
#include <string>
+#include "vm_payload.h"
+
using aidl::android::hardware::security::dice::BccHandover;
using aidl::android::security::dice::IDiceNode;
-using aidl::android::system::virtualmachineservice::IVirtualMachineService;
-
using android::base::ErrnoError;
using android::base::Error;
using android::base::Result;
@@ -133,23 +132,11 @@
auto testService = ndk::SharedRefBase::make<TestService>();
auto callback = []([[maybe_unused]] void* param) {
- // Tell microdroid_manager that we're ready.
- // If we can't, abort in order to fail fast - the host won't proceed without
- // receiving the onReady signal.
- ndk::SpAIBinder binder(
- RpcClient(VMADDR_CID_HOST, IVirtualMachineService::VM_BINDER_SERVICE_PORT));
- auto virtualMachineService = IVirtualMachineService::fromBinder(binder);
- if (virtualMachineService == nullptr) {
- std::cerr << "failed to connect VirtualMachineService\n";
- abort();
- }
- if (auto status = virtualMachineService->notifyPayloadReady(); !status.isOk()) {
- std::cerr << "failed to notify payload ready to virtualizationservice: "
- << status.getDescription() << std::endl;
+ if (!notify_payload_ready()) {
+ std::cerr << "failed to notify payload ready to virtualizationservice" << std::endl;
abort();
}
};
-
if (!RunRpcServerCallback(testService->asBinder().get(), testService->SERVICE_PORT, callback,
nullptr)) {
return Error() << "RPC Server failed to run";
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 2f4f731..0551229 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -27,6 +27,7 @@
"libandroid_logger",
"libanyhow",
"libapkverify",
+ "libbase_rust",
"libbinder_rs",
"libcommand_fds",
"libdisk",
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachinePayloadConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachinePayloadConfig.aidl
index 0c83349..4d37848 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachinePayloadConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachinePayloadConfig.aidl
@@ -24,11 +24,6 @@
@utf8InCpp String payloadPath;
/**
- * Whether to export tombstones (VM crash details) from the VM to the host.
- */
- boolean exportTombstones;
-
- /**
* Command-line style arguments to be passed to the payload when it is executed.
* TODO(b/249064104): Remove this
*/
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 22418b9..563fab0 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -667,7 +667,7 @@
apexes: vec![],
extra_apks: vec![],
prefer_staged: false,
- export_tombstones: payload_config.exportTombstones,
+ export_tombstones: false,
enable_authfs: false,
}
}
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 82a9e78..ff1116a 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -41,6 +41,9 @@
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
use tombstoned_client::{TombstonedConnection, DebuggerdDumpType};
+/// external/crosvm
+use base::UnixSeqpacketListener;
+
const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm";
/// Version of the platform that crosvm currently implements. The format follows SemVer. This
@@ -139,7 +142,8 @@
let (failure_pipe_read, failure_pipe_write) = create_pipe()?;
// If this fails and returns an error, `self` will be left in the `Failed` state.
- let child = Arc::new(run_vm(config, failure_pipe_write)?);
+ let child =
+ Arc::new(run_vm(config, &instance.temporary_directory, failure_pipe_write)?);
let child_clone = child.clone();
let instance_clone = instance.clone();
@@ -425,7 +429,11 @@
}
/// Starts an instance of `crosvm` to manage a new VM.
-fn run_vm(config: CrosvmConfig, failure_pipe_write: File) -> Result<SharedChild, Error> {
+fn run_vm(
+ config: CrosvmConfig,
+ temporary_directory: &Path,
+ failure_pipe_write: File,
+) -> Result<SharedChild, Error> {
validate_config(&config)?;
let mut command = Command::new(CROSVM_PATH);
@@ -434,6 +442,7 @@
.arg("--extended-status")
.arg("run")
.arg("--disable-sandbox")
+ .arg("--no-balloon")
.arg("--cid")
.arg(config.cid.to_string());
@@ -514,6 +523,11 @@
command.arg(add_preserved_fd(&mut preserved_fds, kernel));
}
+ let control_server_socket =
+ UnixSeqpacketListener::bind(temporary_directory.join("crosvm.sock"))
+ .context("failed to create control server")?;
+ command.arg("--socket").arg(add_preserved_fd(&mut preserved_fds, &control_server_socket));
+
debug!("Preserving FDs {:?}", preserved_fds);
command.preserved_fds(preserved_fds);
@@ -546,7 +560,7 @@
/// Adds the file descriptor for `file` to `preserved_fds`, and returns a string of the form
/// "/proc/self/fd/N" where N is the file descriptor.
-fn add_preserved_fd(preserved_fds: &mut Vec<RawFd>, file: &File) -> String {
+fn add_preserved_fd(preserved_fds: &mut Vec<RawFd>, file: &dyn AsRawFd) -> String {
let fd = file.as_raw_fd();
preserved_fds.push(fd);
format!("/proc/self/fd/{}", fd)
diff --git a/virtualizationservice/src/payload.rs b/virtualizationservice/src/payload.rs
index 3efd7ac..82aa760 100644
--- a/virtualizationservice/src/payload.rs
+++ b/virtualizationservice/src/payload.rs
@@ -165,7 +165,6 @@
let payload_metadata = match &app_config.payload {
Payload::PayloadConfig(payload_config) => PayloadMetadata::config(PayloadConfig {
payload_binary_path: payload_config.payloadPath.clone(),
- export_tombstones: payload_config.exportTombstones,
args: payload_config.args.clone().into(),
..Default::default()
}),