A stopped VM with no files is deleted
I came across this accidentally while writing a demo app. It's very
much an edge case, but without this fix quite difficult to get out of.
(Writing the test to reproduce was most of the work here.)
Bug: 243512240
Test: atest MicrodroidTests
Change-Id: Ifd51b463302418d36c83a25c1d5dedb701179363
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index 7c7f4b5..ffb2e14 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -621,15 +621,26 @@
}
virtualMachine = mVirtualMachine;
}
+
+ int status;
if (virtualMachine == null) {
- return mVmRootPath.exists() ? STATUS_STOPPED : STATUS_DELETED;
+ status = STATUS_STOPPED;
} else {
try {
- return stateToStatus(virtualMachine.getState());
+ status = stateToStatus(virtualMachine.getState());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
+ if (status == STATUS_STOPPED && !mVmRootPath.exists()) {
+ // A VM can quite happily keep running if its backing files have been deleted.
+ // But once it stops, it's gone forever.
+ synchronized (mLock) {
+ dropVm();
+ }
+ return STATUS_DELETED;
+ }
+ return status;
}
private int stateToStatus(@VirtualMachineState int state) {
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index e3c9961..5f9b915 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -24,6 +24,7 @@
"MicrodroidTestNativeLib",
"MicrodroidIdleNativeLib",
"MicrodroidEmptyNativeLib",
+ "MicrodroidExitNativeLib",
"MicrodroidPrivateLinkingNativeLib",
],
jni_uses_platform_apis: true,
@@ -73,6 +74,14 @@
stl: "none",
}
+// A payload that exits immediately on start
+cc_library_shared {
+ name: "MicrodroidExitNativeLib",
+ srcs: ["src/native/exitbinary.cpp"],
+ header_libs: ["vm_payload_headers"],
+ stl: "libc++_static",
+}
+
// A payload which tries to link against libselinux, one of private libraries
cc_library_shared {
name: "MicrodroidPrivateLinkingNativeLib",
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index 7bd5f08..5cd0cb1 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -664,6 +664,40 @@
}
@Test
+ @CddTest(
+ requirements = {
+ "9.17/C-1-1",
+ })
+ public void deleteVmFiles() throws Exception {
+ assumeSupportedKernel();
+
+ VirtualMachineConfig config =
+ newVmConfigBuilder()
+ .setPayloadBinaryName("MicrodroidExitNativeLib.so")
+ .setMemoryMib(minMemoryRequired())
+ .build();
+
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_delete", config);
+ vm.run();
+ // If we explicitly stop a VM, that triggers some tidy up; so for this test we start a VM
+ // that immediately stops itself.
+ while (vm.getStatus() == STATUS_RUNNING) {
+ Thread.sleep(100);
+ }
+
+ // Delete the files without telling VMM. This isn't a good idea, but we can't stop an
+ // app doing it, and we should recover from it.
+ for (File f : vm.getRootDir().listFiles()) {
+ Files.delete(f.toPath());
+ }
+ vm.getRootDir().delete();
+
+ VirtualMachineManager vmm = getVirtualMachineManager();
+ assertThat(vmm.get("test_vm_delete")).isNull();
+ assertThat(vm.getStatus()).isEqualTo(STATUS_DELETED);
+ }
+
+ @Test
@CddTest(requirements = {
"9.17/C-1-1",
})
diff --git a/tests/testapk/src/native/exitbinary.cpp b/tests/testapk/src/native/exitbinary.cpp
new file mode 100644
index 0000000..4b70727
--- /dev/null
+++ b/tests/testapk/src/native/exitbinary.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright 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.
+ */
+
+#include <vm_main.h>
+
+extern "C" int AVmPayload_main() {
+ return 0;
+}