Merge "Use "auto" for microdroid super image size"
diff --git a/authfs/src/fsverity/builder.rs b/authfs/src/fsverity/builder.rs
index 94b9718..dd1bd04 100644
--- a/authfs/src/fsverity/builder.rs
+++ b/authfs/src/fsverity/builder.rs
@@ -39,10 +39,7 @@
         .chunks(HASH_PER_PAGE)
         .map(|chunk| {
             let padding_bytes = (HASH_PER_PAGE - chunk.len()) * HASH_SIZE;
-            Ok(Sha256Hasher::new()?
-                .update_from(chunk)?
-                .update(&vec![0u8; padding_bytes])?
-                .finalize()?)
+            Sha256Hasher::new()?.update_from(chunk)?.update(&vec![0u8; padding_bytes])?.finalize()
         })
         .collect()
 }
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index 0db73e9..60f603f 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -31,7 +31,7 @@
 use std::collections::BTreeMap;
 use std::fs::File;
 use std::io::Read;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 use std::sync::{Arc, Mutex};
 use structopt::StructOpt;
 
@@ -225,9 +225,9 @@
 }
 
 fn new_config_local_ro_file(
-    protected_file: &PathBuf,
-    merkle_tree_dump: &PathBuf,
-    signature: &PathBuf,
+    protected_file: &Path,
+    merkle_tree_dump: &Path,
+    signature: &Path,
 ) -> Result<FileConfig> {
     let file = File::open(&protected_file)?;
     let file_size = file.metadata()?.len();
@@ -241,7 +241,7 @@
     Ok(FileConfig::LocalVerifiedReadonlyFile { reader, file_size })
 }
 
-fn new_config_local_ro_file_unverified(file_path: &PathBuf) -> Result<FileConfig> {
+fn new_config_local_ro_file_unverified(file_path: &Path) -> Result<FileConfig> {
     let reader = LocalFileReader::new(File::open(file_path)?)?;
     let file_size = reader.len();
     Ok(FileConfig::LocalUnverifiedReadonlyFile { reader, file_size })
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
index 3837dd3..6a24011 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
@@ -23,6 +23,7 @@
 
 import android.platform.test.annotations.RootPermissionTest;
 
+import com.android.compatibility.common.util.PollingCheck;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
@@ -53,7 +54,10 @@
     private static final String AUTHFS_BIN = "/apex/com.android.virt/bin/authfs";
 
     /** Plenty of time for authfs to get ready */
-    private static final int TIME_BUDGET_AUTHFS_SETUP = 1500;  // ms
+    private static final int AUTHFS_INIT_TIMEOUT_MS = 1500;
+
+    /** FUSE's magic from statfs(2) */
+    private static final String FUSE_SUPER_MAGIC_HEX = "65735546";
 
     private ITestDevice mDevice;
     private ExecutorService mThreadPool;
@@ -68,7 +72,7 @@
     public void tearDown() throws DeviceNotAvailableException {
         mDevice.executeShellV2Command("killall authfs fd_server");
         mDevice.executeShellV2Command("umount " + MOUNT_DIR);
-        mDevice.executeShellV2Command("rm -f " + TEST_DIR);
+        mDevice.executeShellV2Command("rm -f " + TEST_DIR + "/output");
     }
 
     @Test
@@ -81,7 +85,6 @@
                 + " --local-ro-file 5:input.4k1:input.4k1.merkle_dump:input.4k1.fsv_sig:cert.der"
                 + " --local-ro-file 6:input.4k:input.4k.merkle_dump:input.4k.fsv_sig:cert.der"
         );
-        Thread.sleep(TIME_BUDGET_AUTHFS_SETUP);
 
         // Action
         String actualHashUnverified4m = computeFileHashInGuest(MOUNT_DIR + "/3");
@@ -111,7 +114,6 @@
         runAuthFsInBackground(
                 "--remote-ro-file-unverified 10:6:4194304 --remote-ro-file 11:3:4194304:cert.der"
         );
-        Thread.sleep(TIME_BUDGET_AUTHFS_SETUP);
 
         // Action
         String actualHashUnverified4m = computeFileHashInGuest(MOUNT_DIR + "/10");
@@ -138,7 +140,6 @@
         runAuthFsInBackground(
                 "--remote-ro-file 10:3:4096:cert.der --remote-ro-file 11:6:4097:cert.der"
         );
-        Thread.sleep(TIME_BUDGET_AUTHFS_SETUP);
 
         // Action
         String actualHash4k = computeFileHashInGuest(MOUNT_DIR + "/10");
@@ -161,7 +162,6 @@
                 "--ro-fds 3:4:5"
         );
         runAuthFsInBackground("--remote-ro-file 10:3:4096:cert.der");
-        Thread.sleep(TIME_BUDGET_AUTHFS_SETUP);
 
         // Verify
         assertFalse(copyFileInGuest(MOUNT_DIR + "/10", "/dev/null"));
@@ -173,7 +173,6 @@
         // Setup
         runFdServerInBackground("3<>output", "--rw-fds 3");
         runAuthFsInBackground("--remote-new-rw-file 20:3");
-        Thread.sleep(TIME_BUDGET_AUTHFS_SETUP);
 
         // Action
         String srcPath = "/system/bin/linker";
@@ -195,9 +194,8 @@
     public void testWriteFailedIfDetectsTampering()
             throws DeviceNotAvailableException, InterruptedException {
         // Setup
-        runFdServerInBackground("3<>/output", "--rw-fds 3");
+        runFdServerInBackground("3<>output", "--rw-fds 3");
         runAuthFsInBackground("--remote-new-rw-file 20:3");
-        Thread.sleep(TIME_BUDGET_AUTHFS_SETUP);
 
         String srcPath = "/system/bin/linker";
         String destPath = MOUNT_DIR + "/20";
@@ -249,6 +247,17 @@
         }
     }
 
+    private void throwDowncastedException(Exception e) throws DeviceNotAvailableException {
+        if (e instanceof DeviceNotAvailableException) {
+            throw (DeviceNotAvailableException) e;
+        } else {
+            // 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);
+        }
+    }
+
     private void runAuthFsInBackground(String flags) throws DeviceNotAvailableException {
         String cmd = "cd " + TEST_DIR + " && " + AUTHFS_BIN + " " + MOUNT_DIR + " " + flags;
 
@@ -261,6 +270,11 @@
                 throw new RuntimeException(e);
             }
         });
+        try {
+            PollingCheck.waitFor(AUTHFS_INIT_TIMEOUT_MS, () -> isRemoteDirectoryOnFuse(MOUNT_DIR));
+        } catch (Exception e) {
+            throwDowncastedException(e);
+        }
     }
 
     private void runFdServerInBackground(String execParamsForOpeningFds, String flags)
@@ -278,6 +292,11 @@
         });
     }
 
+    private boolean isRemoteDirectoryOnFuse(String path) throws DeviceNotAvailableException {
+        String fs_type = expectRemoteCommandToSucceed("stat -f -c '%t' " + path);
+        return FUSE_SUPER_MAGIC_HEX.equals(fs_type);
+    }
+
     private String expectRemoteCommandToSucceed(String cmd) throws DeviceNotAvailableException {
         CommandResult result = mDevice.executeShellV2Command(cmd);
         assertEquals("`" + cmd + "` failed: " + result.getStderr(), CommandStatus.SUCCESS,
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 82be5ae..8ffb3cb 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -35,7 +35,7 @@
 android_filesystem {
     name: "microdroid",
     use_avb: true,
-    avb_private_key: "microdroid.pem",
+    avb_private_key: ":avb_testkey_rsa4096",
     avb_algorithm: "SHA256_RSA4096",
     partition_name: "system",
     deps: [
@@ -103,7 +103,7 @@
             ],
         },
     },
-    avb_private_key: "microdroid.pem",
+    avb_private_key: ":avb_testkey_rsa4096",
     avb_algorithm: "SHA256_RSA4096",
     file_contexts: "microdroid_vendor_file_contexts",
 }
@@ -158,7 +158,7 @@
     header_version: "4",
     partition_name: "boot",
     use_avb: true,
-    avb_private_key: "microdroid.pem",
+    avb_private_key: ":avb_testkey_rsa4096",
 }
 
 android_filesystem {
@@ -187,7 +187,7 @@
     vendor_boot: true,
     partition_name: "vendor_boot",
     use_avb: true,
-    avb_private_key: "microdroid.pem",
+    avb_private_key: ":avb_testkey_rsa4096",
 }
 
 android_filesystem {
@@ -271,7 +271,7 @@
 vbmeta {
     name: "microdroid_vbmeta",
     partition_name: "vbmeta",
-    private_key: "microdroid.pem",
+    private_key: ":avb_testkey_rsa4096",
     partitions: [
         "microdroid_vendor",
         "microdroid_vendor_boot-5.10",
@@ -280,12 +280,12 @@
         {
             name: "vbmeta_system",
             rollback_index_location: 1,
-            private_key: "microdroid.pem",
+            private_key: ":avb_testkey_rsa4096",
         },
         {
             name: "boot",
             rollback_index_location: 2,
-            private_key: "microdroid.pem",
+            private_key: ":avb_testkey_rsa4096",
         },
     ],
 }
@@ -293,7 +293,7 @@
 vbmeta {
     name: "microdroid_vbmeta_system",
     partition_name: "vbmeta_system",
-    private_key: "microdroid.pem",
+    private_key: ":avb_testkey_rsa4096",
     partitions: [
         "microdroid",
     ],
diff --git a/microdroid/microdroid.pem b/microdroid/microdroid.pem
deleted file mode 100644
index 2c9de45..0000000
--- a/microdroid/microdroid.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKAIBAAKCAgEAwOn8PTeXaSbNtTx0iJspX7ZJn1dUWHeDtq/r6HPTCEnFuYqV
-c4foSjbF3nIR5T5ZxLeAcTYUl2A/mieUYKG7DkySf5LGlAgWOn4/UXSl14fvESBK
-XA51w9Stg3GyWycYYVkz52mDWfkfoic2UVQJXpsTTcAFDR+u8e5oDxscgLbU7/g2
-mbOv7E4B5n6ItscmjxzRh1IvFVRlPzzyDYQ8UF3JDk0Ndoz7Pze5hF72zbkH6Dmh
-zS91xZFuIJNPjEEjr4Nli9xADhEdsRPeJoFQa+XafGabR2Z2jbVk1UqkD0yxvDhw
-jQaSQ2P6W/flzJnQgRr21FHONU9B6sh+p2ulK3Huda7JIdCExA2SsP+2PVE1Kw9G
-s+85o1/8rfa576qwSLR++ztvZwg3DF/4UQME2oqGLM9TLyj8bdN3Co4XSwsJ7pru
-1WgyQ6S8qLrGld4oxxTbQYoVIhEjUjiOHSKggflZ76Mscnh5hv6ZnuhpbV0b0zSB
-95hP/fbc7VP3ru6ArHA3HF84MQ6i60YPrB5ZpB39wbex1AynbGG0bHw9kgo7L4bF
-rE6p7T6cVazWd0LcWE+WgBHXU2dcVtMPmxUTyzhoeTSVv6eDqagTCe0q3IBZ9h4c
-5q7O6Ipbu/3ckEeRlzBFp7ymjGosqZL0siYtNSKo+FJxvEtbcEiz8xKU6ksCAwEA
-AQKCAgABk+hUN9gtK/wSt2PcmihEeCEyBGzULGYPYHD1fb0EHFZZpiCCyWHE+Z20
-rAopbdEeIsISKvcQJeFfSWOVRr1Y6JdqI7EsFMTZxZ6O8/1HPf1ejn0NM7ES4PJq
-WMu0oyU35preEabZXYg8s0VmdOF+vJXz1NY1vUSZBfIj7XzWMKidLdKEf0u5CwpI
-8E/97rMKCJpOGQX5jq8lTUJNif9USPAUBd1OUujbI4GI653pINJHu/Du1KyJ6R6V
-dZ747q90CAcUlcCQXON5R0hR2EzoFQEsc0ph0xy1G/6vH+UnmuD5QRzqils34eFy
-Txy6B6gE5kuOgPzGwtcvLGScyeyX4rjA1RS+Zst8PQG35D2WSLENbITDZ2u2n146
-C/TWWd6F/qE8cSCI8/ZQ5iEarw8Hp3sVe7KRqwG0n2ESlW/px6E9DmzbjgW/BLwB
-diBLmHVmOT5BZPqbjNiwsX/aLmf7cjtnttFuadf2roaa6jlyQzsqRyVzJKH2H9iD
-DFeakMAjngVW0XnWrpgehoG5JfWDbXWcg8Kt5ZlegSOs1KGBAj69RkotJWjQ2rlP
-TijMuHgXVUIvqQp5BIc8UnVqX3kzljLd2eVM0K6ZcPnh+yBhk/Jd88whxkONgbs4
-g8EQvov4c8UL1Sx2ZTDDCN0WK0dHG1IhtB/E014yFIq/qqQMQQKCAQEA8c6tw7o+
-36Uh1uB5oEw6zSsxY7x13cW9tis9slD4/bXiXJwTOjs1/+S+wrmJ4UoOarygFgh0
-xWc1YGs20UWBkO4T9jaZJBqESH96RvQn7ubBEHYECClPhRuwbWWc+UzHjgusmuYl
-xPtY9MgtI5AF6O2MEqzCWbLZzFhhe/V6FKKcyV3aq6vOEjCeTz0/ntfcfS08T/5l
-kayny1nlVju3eig5WK30+X66rN0+eYS1PqLInlzSpFGWMJA6s+uI+1ze506QWKFI
-vNPf4Wtm8VFksRSbrYBriKg63UsgOGfaq5zmgJ5Yst8tIheKLJE5meLjGcea8Dhe
-gz+Jtj+Es9WdVQKCAQEAzDymkIOrwh8j45zFuH+6vataWkGusloV3P6dE0V5NDPH
-Oa/EfOqZ3qQKiS8bHRcdEwRQK+TSeOWi6phDQVu2lzwBA1PySxz7T1T38RKP9myu
-yPFah6bLKntvE36fj3U7RaU2gf8GXGMuwoRWoGDQNHQ+YusKOFreht7dlclqLKKp
-Xct7lKrg3nOtJI3P/jxXZwE/+UenqS5M8QOd7X0/77d9VJBYAhUZ9A5cOuk327e+
-zxEeg23UndcqmJRj0zsxYR+d/NaDV9yf8usAHRv/S3nwVN6YkmmnVNY46ukCNlxV
-G9VZcM1PNWFu3MdGs7f8u973DXYMC3Et3JD8/XNpHwKCAQEAoN8g6dOx2raRAdUL
-9eJhSHhQ/oq2W9ofsd+ox0ZpsNleBMDtZdNYxKsZYpFvssvDNa3ST3ZGwcI9V61c
-pfO5FIPgEfEXB1cA5P6yihwLqTwp+9qYKhnZxrO6N73fplg3d0A7ED7Hp/qUnx3C
-MiOyWtoXU6FOF8EOd43hJUgWdT4OU0W2onnFuoJQAGW+Ot5f2YPL2WhliHo2k+p1
-0l4eXg3+wQnSrl5qsXDsDIqGzmocUIduuFVzN1HtTCAKXTjXL0osbFq4+q93BhCf
-RzMga1NLky2Z3SwXJXzCreQiLgRPsggm+LHT5BeHHMltafzxnAgZA3JtJqKR6wbt
-MXuPlQKCAQBRaWvL93Cj9N3NgVSfcurxaDif+ca1eYt1iFj8sZja4jjsGdBtKucj
-BKjeflSEiA4i4nlCxffQEVmbwg4tI23pYxXUScYvguH6gZhEFG4mgLQ7jVnU8PvT
-d1iToMnh2Y1C+dR8hTf34+Fj0HTngS/9eMjVjXOJe09w263gY2VbXYu9w7sDTApP
-IjVGePEMGumMOcdSiumeWsJ44EQ3wtGVsMLYzqEGU3sA+ihxoz2M6/W3fM94/HLg
-aECmiaAxN0s7t+q2Opwojd2Ea4CkGeDuKTFP41tWIZ9+BdaybSdgm/WhHq6EdJK6
-aJhUkWrxgMnR6dL5HrE2bmslnPX74eFpAoIBAE4epxTb9SstOxhwFA1Vvrz5G64k
-n61OgXj49QPpBcBOGXVH1l8L6YQJO+bCVESiACn2Q/lJ8mp6lRI2KtsemUfsa5X7
-XV1qldtTFobMmB4JZDTEM882TWainK2+yhGfEGWrdeCWDBSWqQj/VLpAUqQqdJFU
-+GRvPQV+DSk4Az7zGpyrtjpiZYbRjWHBT2FAwSRS/iqEHjKrvrCqlaq16Un0JJFb
-0ztbCC2tDaVlwpojPnPE2sgojYeGQ0ghB1os9K4RFaoW9l+Pfye5vJ9G0/wmjVhY
-HQaN/WJp8k79mXacCQAD51xtWnJePGd4D5WbA855jo0Dov/mOtwf0Ahbl6w=
------END RSA PRIVATE KEY-----
diff --git a/tests/vsock_test.cc b/tests/vsock_test.cc
index 57a03ca..0df1c5d 100644
--- a/tests/vsock_test.cc
+++ b/tests/vsock_test.cc
@@ -27,10 +27,10 @@
 #include "android-base/logging.h"
 #include "android-base/parseint.h"
 #include "android-base/unique_fd.h"
-
 #include "virt/VirtualizationTest.h"
 
 using namespace android::base;
+using namespace android::os;
 
 namespace virt {
 
@@ -58,7 +58,9 @@
     ASSERT_EQ(ret, 0) << strerror(errno);
 
     sp<IVirtualMachine> vm;
-    status = mVirtManager->startVm(String16(kVmConfigPath), std::nullopt, &vm);
+    unique_fd vm_config_fd(open(kVmConfigPath, O_RDONLY | O_CLOEXEC));
+    status =
+            mVirtManager->startVm(ParcelFileDescriptor(std::move(vm_config_fd)), std::nullopt, &vm);
     ASSERT_TRUE(status.isOk()) << "Error starting VM: " << status;
 
     int32_t cid;
diff --git a/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl b/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl
index ab03c18..c2fc719 100644
--- a/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl
+++ b/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl
@@ -23,7 +23,8 @@
      * Start the VM with the given config file, and return a handle to it. If `logFd` is provided
      * then console logs from the VM will be sent to it.
      */
-    IVirtualMachine startVm(String configPath, in @nullable ParcelFileDescriptor logFd);
+    IVirtualMachine startVm(
+            in ParcelFileDescriptor configFd, in @nullable ParcelFileDescriptor logFd);
 
     /**
      * Get a list of all currently running VMs. This method is only intended for debug purposes,
diff --git a/virtmanager/aidl/android/system/virtmanager/VirtualMachineDebugInfo.aidl b/virtmanager/aidl/android/system/virtmanager/VirtualMachineDebugInfo.aidl
index d877a56..d1ba9a6 100644
--- a/virtmanager/aidl/android/system/virtmanager/VirtualMachineDebugInfo.aidl
+++ b/virtmanager/aidl/android/system/virtmanager/VirtualMachineDebugInfo.aidl
@@ -19,10 +19,13 @@
 parcelable VirtualMachineDebugInfo {
     /** The CID assigned to the VM. */
     int cid;
-
+    /** The UID of the process which requested the VM. */
+    int requester_uid;
+    /** The SID of the process which requested the VM. */
+    @nullable String requester_sid;
     /**
-     * The filename of the config file used to start the VM. This may have changed since it was
-     * read so it shouldn't be trusted; it is only stored for debugging purposes.
+     * The PID of the process which requested the VM. Note that this process may no longer exist and
+     * the PID may have been reused for a different process, so this should not be trusted.
      */
-    String configPath;
+    int requester_pid;
 }
diff --git a/virtmanager/src/aidl.rs b/virtmanager/src/aidl.rs
index 8c963d2..98af714 100644
--- a/virtmanager/src/aidl.rs
+++ b/virtmanager/src/aidl.rs
@@ -27,6 +27,7 @@
     self, Interface, ParcelFileDescriptor, StatusCode, Strong, ThreadState,
 };
 use log::error;
+use std::ffi::CStr;
 use std::fs::File;
 use std::sync::{Arc, Mutex, Weak};
 
@@ -50,7 +51,7 @@
     /// Returns a binder `IVirtualMachine` object referring to it, as a handle for the client.
     fn startVm(
         &self,
-        config_path: &str,
+        config_fd: &ParcelFileDescriptor,
         log_fd: Option<&ParcelFileDescriptor>,
     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
         let state = &mut *self.state.lock().unwrap();
@@ -58,7 +59,25 @@
         let log_fd = log_fd
             .map(|fd| fd.as_ref().try_clone().map_err(|_| StatusCode::UNKNOWN_ERROR))
             .transpose()?;
-        let instance = Arc::new(start_vm(config_path, cid, log_fd)?);
+        let requester_uid = ThreadState::get_calling_uid();
+        let requester_sid = ThreadState::with_calling_sid(|sid| {
+            sid.and_then(|sid: &CStr| match sid.to_str() {
+                Ok(s) => Some(s.to_owned()),
+                Err(e) => {
+                    error!("SID was not valid UTF-8: {:?}", e);
+                    None
+                }
+            })
+        });
+        let requester_pid = ThreadState::get_calling_pid();
+        let instance = Arc::new(start_vm(
+            config_fd.as_ref(),
+            cid,
+            log_fd,
+            requester_uid,
+            requester_sid,
+            requester_pid,
+        )?);
         // TODO(qwandor): keep track of which CIDs are currently in use so that we can reuse them.
         state.next_cid = state.next_cid.checked_add(1).ok_or(StatusCode::UNKNOWN_ERROR)?;
         state.add_vm(Arc::downgrade(&instance));
@@ -78,7 +97,9 @@
             .into_iter()
             .map(|vm| VirtualMachineDebugInfo {
                 cid: vm.cid as i32,
-                configPath: vm.config_path.clone(),
+                requester_uid: vm.requester_uid as i32,
+                requester_sid: vm.requester_sid.clone(),
+                requester_pid: vm.requester_pid,
             })
             .collect();
         Ok(cids)
@@ -191,15 +212,23 @@
     }
 }
 
-/// Start a new VM instance from the given VM config filename. This assumes the VM is not already
+/// Start a new VM instance from the given VM config file. This assumes the VM is not already
 /// running.
-fn start_vm(config_path: &str, cid: Cid, log_fd: Option<File>) -> binder::Result<VmInstance> {
-    let config = VmConfig::load(config_path).map_err(|e| {
-        error!("Failed to load VM config {}: {:?}", config_path, e);
+fn start_vm(
+    config_file: &File,
+    cid: Cid,
+    log_fd: Option<File>,
+    requester_uid: u32,
+    requester_sid: Option<String>,
+    requester_pid: i32,
+) -> binder::Result<VmInstance> {
+    let config = VmConfig::load(config_file).map_err(|e| {
+        error!("Failed to load VM config from {:?}: {:?}", config_file, e);
         StatusCode::BAD_VALUE
     })?;
-    Ok(VmInstance::start(&config, cid, config_path, log_fd).map_err(|e| {
-        error!("Failed to start VM {}: {:?}", config_path, e);
-        StatusCode::UNKNOWN_ERROR
-    })?)
+    Ok(VmInstance::start(&config, cid, log_fd, requester_uid, requester_sid, requester_pid)
+        .map_err(|e| {
+            error!("Failed to start VM from {:?}: {:?}", config_file, e);
+            StatusCode::UNKNOWN_ERROR
+        })?)
 }
diff --git a/virtmanager/src/config.rs b/virtmanager/src/config.rs
index d8cb06f..0b12c0b 100644
--- a/virtmanager/src/config.rs
+++ b/virtmanager/src/config.rs
@@ -14,7 +14,7 @@
 
 //! Function and types for VM configuration.
 
-use anyhow::{bail, Context, Error};
+use anyhow::{bail, Error};
 use serde::{Deserialize, Serialize};
 use std::fs::File;
 use std::io::BufReader;
@@ -51,8 +51,7 @@
     }
 
     /// Load the configuration for a VM from the given JSON file.
-    pub fn load(path: &str) -> Result<VmConfig, Error> {
-        let file = File::open(path).with_context(|| format!("Failed to open {}", path))?;
+    pub fn load(file: &File) -> Result<VmConfig, Error> {
         let buffered = BufReader::new(file);
         Ok(serde_json::from_reader(buffered)?)
     }
diff --git a/virtmanager/src/crosvm.rs b/virtmanager/src/crosvm.rs
index 814a1a7..bef9982 100644
--- a/virtmanager/src/crosvm.rs
+++ b/virtmanager/src/crosvm.rs
@@ -30,15 +30,25 @@
     child: Child,
     /// The CID assigned to the VM for vsock communication.
     pub cid: Cid,
-    /// The filename of the config file that was used to start the VM. This may have changed since
-    /// it was read so it shouldn't be trusted; it is only stored for debugging purposes.
-    pub config_path: String,
+    /// The UID of the process which requested the VM.
+    pub requester_uid: u32,
+    /// The SID of the process which requested the VM.
+    pub requester_sid: Option<String>,
+    /// The PID of the process which requested the VM. Note that this process may no longer exist
+    /// and the PID may have been reused for a different process, so this should not be trusted.
+    pub requester_pid: i32,
 }
 
 impl VmInstance {
     /// Create a new `VmInstance` for the given process.
-    fn new(child: Child, cid: Cid, config_path: &str) -> VmInstance {
-        VmInstance { child, cid, config_path: config_path.to_owned() }
+    fn new(
+        child: Child,
+        cid: Cid,
+        requester_uid: u32,
+        requester_sid: Option<String>,
+        requester_pid: i32,
+    ) -> VmInstance {
+        VmInstance { child, cid, requester_uid, requester_sid, requester_pid }
     }
 
     /// Start an instance of `crosvm` to manage a new VM. The `crosvm` instance will be killed when
@@ -46,11 +56,13 @@
     pub fn start(
         config: &VmConfig,
         cid: Cid,
-        config_path: &str,
         log_fd: Option<File>,
+        requester_uid: u32,
+        requester_sid: Option<String>,
+        requester_pid: i32,
     ) -> Result<VmInstance, Error> {
         let child = run_vm(config, cid, log_fd)?;
-        Ok(VmInstance::new(child, cid, config_path))
+        Ok(VmInstance::new(child, cid, requester_uid, requester_sid, requester_pid))
     }
 }
 
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 8c2a084..1f39e4a 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -77,9 +77,12 @@
     daemonize: bool,
 ) -> Result<(), Error> {
     let config_filename = config_path.to_str().context("Failed to parse VM config path")?;
+    let config_file = ParcelFileDescriptor::new(
+        File::open(config_filename).context("Failed to open config file")?,
+    );
     let stdout_file = ParcelFileDescriptor::new(duplicate_stdout()?);
     let stdout = if daemonize { None } else { Some(&stdout_file) };
-    let vm = virt_manager.startVm(config_filename, stdout).context("Failed to start VM")?;
+    let vm = virt_manager.startVm(&config_file, stdout).context("Failed to start VM")?;
 
     let cid = vm.getCid().context("Failed to get CID")?;
     println!("Started VM from {} with CID {}.", config_filename, cid);