[rpc_binder] Create RpcServer with new_bound_socket
This cl is part of the change to replace the init-dependent binder
API ARpcServer_newInitUnixDomain with a more generic version
ARpcServer_newBoundSocket.
Test: atest microdroid_manager_test
Test: m authfs_service
Bug: 275729094
Change-Id: I6e54166f919f0033bf5357cd0e1ece4e9d54f763
diff --git a/authfs/service/Android.bp b/authfs/service/Android.bp
index de6326d..2101a36 100644
--- a/authfs/service/Android.bp
+++ b/authfs/service/Android.bp
@@ -17,6 +17,7 @@
"liblog_rust",
"libnix",
"librpcbinder_rs",
+ "librustutils",
"libshared_child",
],
prefer_rlib: true,
diff --git a/authfs/service/src/main.rs b/authfs/service/src/main.rs
index e710f07..78de07a 100644
--- a/authfs/service/src/main.rs
+++ b/authfs/service/src/main.rs
@@ -25,8 +25,10 @@
use anyhow::{bail, Result};
use log::*;
use rpcbinder::RpcServer;
+use rustutils::sockets::android_get_control_socket;
use std::ffi::OsString;
use std::fs::{create_dir, read_dir, remove_dir_all, remove_file};
+use std::os::unix::io::{FromRawFd, OwnedFd};
use std::sync::atomic::{AtomicUsize, Ordering};
use authfs_aidl_interface::aidl::com::android::virt::fs::AuthFsConfig::AuthFsConfig;
@@ -106,6 +108,25 @@
Ok(())
}
+/// Prepares a socket file descriptor for the authfs service.
+///
+/// # Safety requirement
+///
+/// The caller must ensure that this function is the only place that claims ownership
+/// of the file descriptor and it is called only once.
+unsafe fn prepare_authfs_service_socket() -> Result<OwnedFd> {
+ let raw_fd = android_get_control_socket(AUTHFS_SERVICE_SOCKET_NAME)?;
+
+ // Creating OwnedFd for stdio FDs is not safe.
+ if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
+ bail!("File descriptor {raw_fd} is standard I/O descriptor");
+ }
+ // SAFETY: Initializing OwnedFd for a RawFd created by the init.
+ // We checked that the integer value corresponds to a valid FD and that the caller
+ // ensures that this is the only place to claim its ownership.
+ Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
+}
+
fn try_main() -> Result<()> {
let debuggable = env!("TARGET_BUILD_VARIANT") != "user";
let log_level = if debuggable { log::Level::Trace } else { log::Level::Info };
@@ -115,9 +136,11 @@
clean_up_working_directory()?;
+ // SAFETY: This is the only place we take the ownership of the fd of the authfs service.
+ let socket_fd = unsafe { prepare_authfs_service_socket()? };
let service = AuthFsService::new_binder(debuggable).as_binder();
debug!("{} is starting as a rpc service.", AUTHFS_SERVICE_SOCKET_NAME);
- let server = RpcServer::new_init_unix_domain(service, AUTHFS_SERVICE_SOCKET_NAME)?;
+ let server = RpcServer::new_bound_socket(service, socket_fd)?;
info!("The RPC server '{}' is running.", AUTHFS_SERVICE_SOCKET_NAME);
server.join();
info!("The RPC server at '{}' has shut down gracefully.", AUTHFS_SERVICE_SOCKET_NAME);
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index be86247..8fa2807 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -58,6 +58,7 @@
use std::io::{Read, Write};
use std::os::unix::process::CommandExt;
use std::os::unix::process::ExitStatusExt;
+use std::os::unix::io::{FromRawFd, OwnedFd};
use std::path::Path;
use std::process::{Child, Command, Stdio};
use std::str;
@@ -190,23 +191,36 @@
})
}
-fn prepare_vm_payload_service_socket() -> Result<()> {
- android_get_control_socket(VM_PAYLOAD_SERVICE_SOCKET_NAME)?;
+/// Prepares a socket file descriptor for the vm payload service.
+///
+/// # Safety requirement
+///
+/// The caller must ensure that this function is the only place that claims ownership
+/// of the file descriptor and it is called only once.
+unsafe fn prepare_vm_payload_service_socket() -> Result<OwnedFd> {
+ let raw_fd = android_get_control_socket(VM_PAYLOAD_SERVICE_SOCKET_NAME)?;
- // TODO(b/275729094): Convert the obtained file descriptor to `OwnedFd`
- // and use it to set the `RpcServer`.
- Ok(())
+ // Creating OwnedFd for stdio FDs is not safe.
+ if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
+ bail!("File descriptor {raw_fd} is standard I/O descriptor");
+ }
+ // SAFETY: Initializing OwnedFd for a RawFd created by the init.
+ // We checked that the integer value corresponds to a valid FD and that the caller
+ // ensures that this is the only place to claim its ownership.
+ Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
}
fn try_main() -> Result<()> {
let _ignored = kernlog::init();
info!("started.");
+ // SAFETY: This is the only place we take the ownership of the fd of the vm payload service.
+ //
// To ensure that the CLOEXEC flag is set on the file descriptor as early as possible,
// it is necessary to fetch the socket corresponding to vm_payload_service at the
// very beginning, as android_get_control_socket() sets the CLOEXEC flag on the file
// descriptor.
- prepare_vm_payload_service_socket()?;
+ let vm_payload_service_fd = unsafe { prepare_vm_payload_service_socket()? };
load_crashkernel_if_supported().context("Failed to load crashkernel")?;
@@ -217,7 +231,7 @@
.context("cannot connect to VirtualMachineService")
.map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
- match try_run_payload(&service) {
+ match try_run_payload(&service, vm_payload_service_fd) {
Ok(code) => {
if code == 0 {
info!("task successfully finished");
@@ -331,7 +345,10 @@
Ok(Some(u32::from_be_bytes(log) == 1))
}
-fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
+fn try_run_payload(
+ service: &Strong<dyn IVirtualMachineService>,
+ vm_payload_service_fd: OwnedFd,
+) -> Result<i32> {
let metadata = load_metadata().context("Failed to load payload metadata")?;
let dice = DiceDriver::new(Path::new("/dev/open-dice0")).context("Failed to load DICE")?;
@@ -448,7 +465,12 @@
// Wait until zipfuse has mounted the APKs so we can access the payload
zipfuse.wait_until_done()?;
- register_vm_payload_service(allow_restricted_apis, service.clone(), dice_artifacts)?;
+ register_vm_payload_service(
+ allow_restricted_apis,
+ service.clone(),
+ dice_artifacts,
+ vm_payload_service_fd,
+ )?;
// Wait for encryptedstore to finish mounting the storage (if enabled) before setting
// microdroid_manager.init_done. Reason is init stops uneventd after that.
diff --git a/microdroid_manager/src/vm_payload_service.rs b/microdroid_manager/src/vm_payload_service.rs
index 11e6967..bcddc3a 100644
--- a/microdroid_manager/src/vm_payload_service.rs
+++ b/microdroid_manager/src/vm_payload_service.rs
@@ -23,6 +23,7 @@
use diced_open_dice::{DiceArtifacts, OwnedDiceArtifacts};
use log::{error, info};
use rpcbinder::RpcServer;
+use std::os::unix::io::OwnedFd;
/// Implementation of `IVmPayloadService`.
struct VmPayloadService {
@@ -101,16 +102,14 @@
allow_restricted_apis: bool,
vm_service: Strong<dyn IVirtualMachineService>,
dice: OwnedDiceArtifacts,
+ vm_payload_service_fd: OwnedFd,
) -> Result<()> {
let vm_payload_binder = BnVmPayloadService::new_binder(
VmPayloadService::new(allow_restricted_apis, vm_service, dice),
BinderFeatures::default(),
);
- let server = RpcServer::new_init_unix_domain(
- vm_payload_binder.as_binder(),
- VM_PAYLOAD_SERVICE_SOCKET_NAME,
- )?;
+ let server = RpcServer::new_bound_socket(vm_payload_binder.as_binder(), vm_payload_service_fd)?;
info!("The RPC server '{}' is running.", VM_PAYLOAD_SERVICE_SOCKET_NAME);
// Move server reference into a background thread and run it forever.