summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2020-05-22 00:34:09 +0000
committerAlyssa Ross <hi@alyssa.is>2020-06-15 09:37:34 +0000
commit54ab55f99cd593f71a9b572d23199101a48206ec (patch)
tree633e6f29a69e27db3947fa2b8a857a9ccc2ae638
parente0e035c065fdfbbea1e734d137d87a2635dbd0fa (diff)
downloadcrosvm-54ab55f99cd593f71a9b572d23199101a48206ec.tar
crosvm-54ab55f99cd593f71a9b572d23199101a48206ec.tar.gz
crosvm-54ab55f99cd593f71a9b572d23199101a48206ec.tar.bz2
crosvm-54ab55f99cd593f71a9b572d23199101a48206ec.tar.lz
crosvm-54ab55f99cd593f71a9b572d23199101a48206ec.tar.xz
crosvm-54ab55f99cd593f71a9b572d23199101a48206ec.tar.zst
crosvm-54ab55f99cd593f71a9b572d23199101a48206ec.zip
crosvm: add memfd server
-rw-r--r--Cargo.lock14
-rw-r--r--Cargo.toml1
-rw-r--r--aarch64/Cargo.toml1
-rw-r--r--aarch64/src/lib.rs4
-rw-r--r--arch/Cargo.toml1
-rw-r--r--arch/src/lib.rs24
-rw-r--r--seccomp/aarch64/memfd_server.policy15
-rw-r--r--seccomp/arm/memfd_server.policy15
-rw-r--r--seccomp/x86_64/memfd_server.policy15
-rw-r--r--servers/Cargo.toml11
-rw-r--r--servers/src/jailed.rs142
-rw-r--r--servers/src/lib.rs32
-rw-r--r--servers/src/memfd.rs171
-rw-r--r--src/crosvm.rs2
-rw-r--r--src/linux.rs67
-rw-r--r--src/main.rs6
-rw-r--r--x86_64/Cargo.toml1
-rw-r--r--x86_64/src/lib.rs11
18 files changed, 520 insertions, 13 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a72324e..a1fd8e1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -15,6 +15,7 @@ dependencies = [
  "msg_socket 0.1.0",
  "remain 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "resources 0.1.0",
+ "servers 0.1.0",
  "sync 0.1.0",
  "sys_util 0.1.0",
  "vm_control 0.1.0",
@@ -38,6 +39,7 @@ dependencies = [
  "kvm 0.1.0",
  "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
  "resources 0.1.0",
+ "servers 0.1.0",
  "sync 0.1.0",
  "sys_util 0.1.0",
  "vm_control 0.1.0",
@@ -155,6 +157,7 @@ dependencies = [
  "remain 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "resources 0.1.0",
  "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servers 0.1.0",
  "sync 0.1.0",
  "sys_util 0.1.0",
  "vhost 0.1.0",
@@ -728,6 +731,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "servers"
+version = "0.1.0"
+dependencies = [
+ "io_jail 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msg_socket 0.1.0",
+ "sys_util 0.1.0",
+]
+
+[[package]]
 name = "slab"
 version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -878,6 +891,7 @@ dependencies = [
  "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
  "remain 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "resources 0.1.0",
+ "servers 0.1.0",
  "sync 0.1.0",
  "sys_util 0.1.0",
  "vm_control 0.1.0",
diff --git a/Cargo.toml b/Cargo.toml
index 2615178..f53d5c0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -81,6 +81,7 @@ rand_ish = { path = "rand_ish" }
 remain = "*"
 resources = { path = "resources" }
 serde = "*"
+servers = { path = "servers" }
 sync = { path = "sync" }
 sys_util = "*"
 vhost = { path = "vhost" }
diff --git a/aarch64/Cargo.toml b/aarch64/Cargo.toml
index c87c419..fcb4b30 100644
--- a/aarch64/Cargo.toml
+++ b/aarch64/Cargo.toml
@@ -16,6 +16,7 @@ libc = "*"
 msg_socket = { path = "../msg_socket" }
 remain = "*"
 resources = { path = "../resources" }
+servers = { path = "../servers" }
 sync = { path = "../sync" }
 sys_util = { path = "../sys_util" }
 vm_control = { path = "../vm_control" }
diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs
index 20ce8f8..eb254c4 100644
--- a/aarch64/src/lib.rs
+++ b/aarch64/src/lib.rs
@@ -12,8 +12,8 @@ use std::os::unix::io::FromRawFd;
 use std::sync::Arc;
 
 use arch::{
-    get_serial_cmdline, GetSerialCmdlineError, RunnableLinuxVm, SerialHardware, SerialParameters,
-    VmComponents, VmImage,
+    get_serial_cmdline, jail_servers, GetSerialCmdlineError, RunnableLinuxVm, SerialHardware,
+    SerialParameters, ServerStub, VmComponents, VmImage,
 };
 use devices::{Bus, BusError, MemoryParams, PciAddress, PciConfigMmio, PciDevice, PciInterruptPin};
 use io_jail::Minijail;
diff --git a/arch/Cargo.toml b/arch/Cargo.toml
index 68b40a3..bd97262 100644
--- a/arch/Cargo.toml
+++ b/arch/Cargo.toml
@@ -15,3 +15,4 @@ resources = { path = "../resources" }
 sync = { path = "../sync" }
 sys_util = { path = "../sys_util" }
 vm_control = { path = "../vm_control" }
+servers = { path = "../servers" }
diff --git a/arch/src/lib.rs b/arch/src/lib.rs
index 8f957a8..361e918 100644
--- a/arch/src/lib.rs
+++ b/arch/src/lib.rs
@@ -26,6 +26,7 @@ use devices::{
 use io_jail::Minijail;
 use kvm::{IoeventAddress, Kvm, Vcpu, Vm};
 use resources::SystemAllocator;
+use servers::{JailedServer, Server};
 use sync::Mutex;
 use sys_util::{syslog, EventFd, GuestAddress, GuestMemory, GuestMemoryError};
 use vm_control::VmIrqRequestSocket;
@@ -83,6 +84,7 @@ pub struct RunnableLinuxVm {
     pub irq_chip: Option<File>,
     pub split_irqchip: Option<(Arc<Mutex<devices::Pic>>, Arc<Mutex<devices::Ioapic>>)>,
     pub gsi_relay: Option<Arc<GsiRelay>>,
+    pub servers: Vec<Box<dyn servers::Server>>,
     pub io_bus: Bus,
     pub mmio_bus: Bus,
     pub pid_debug_label_map: BTreeMap<u32, String>,
@@ -95,6 +97,12 @@ pub struct VirtioDeviceStub {
     pub jail: Option<Minijail>,
 }
 
+/// The server and optional jail.
+pub struct ServerStub {
+    pub server: Box<dyn Server>,
+    pub jail: Option<Minijail>,
+}
+
 /// Trait which is implemented for each Linux Architecture in order to
 /// set up the memory, cpus, and system devices and to boot the kernel.
 pub trait LinuxArch {
@@ -122,6 +130,7 @@ pub trait LinuxArch {
         ioapic_device_socket: VmIrqRequestSocket,
         serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
         serial_jail: Option<Minijail>,
+        servers: Vec<ServerStub>,
         create_devices: F,
     ) -> Result<RunnableLinuxVm, Self::Error>
     where
@@ -201,6 +210,21 @@ impl Display for DeviceRegistrationError {
     }
 }
 
+pub fn jail_servers(servers: Vec<ServerStub>) -> Result<Vec<Box<dyn Server>>, servers::ProxyError> {
+    servers
+        .into_iter()
+        .map(|ServerStub { server, jail }| {
+            if let Some(jail) = jail {
+                let preserved_fds = server.keep_fds();
+                let jailed = JailedServer::jail(server, &jail, preserved_fds)?;
+                Ok(Box::new(jailed) as Box<_>)
+            } else {
+                Ok(server)
+            }
+        })
+        .collect()
+}
+
 /// Creates a root PCI device for use by this Vm.
 pub fn generate_pci_root(
     devices: Vec<(Box<dyn PciDevice>, Option<Minijail>)>,
diff --git a/seccomp/aarch64/memfd_server.policy b/seccomp/aarch64/memfd_server.policy
new file mode 100644
index 0000000..d049d00
--- /dev/null
+++ b/seccomp/aarch64/memfd_server.policy
@@ -0,0 +1,15 @@
+# Copyright 2020 Alyssa Ross. All rights reserved.
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+@include /usr/share/policy/crosvm/common_device.policy
+
+accept4: 1
+# F_ADD_SEALS
+fcntl: arg1 == 1033
+ftruncate: 1
+memfd_create: 1
+open: return ENOENT
+openat: return ENOENT
+shutdown: 1
diff --git a/seccomp/arm/memfd_server.policy b/seccomp/arm/memfd_server.policy
new file mode 100644
index 0000000..d049d00
--- /dev/null
+++ b/seccomp/arm/memfd_server.policy
@@ -0,0 +1,15 @@
+# Copyright 2020 Alyssa Ross. All rights reserved.
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+@include /usr/share/policy/crosvm/common_device.policy
+
+accept4: 1
+# F_ADD_SEALS
+fcntl: arg1 == 1033
+ftruncate: 1
+memfd_create: 1
+open: return ENOENT
+openat: return ENOENT
+shutdown: 1
diff --git a/seccomp/x86_64/memfd_server.policy b/seccomp/x86_64/memfd_server.policy
new file mode 100644
index 0000000..d049d00
--- /dev/null
+++ b/seccomp/x86_64/memfd_server.policy
@@ -0,0 +1,15 @@
+# Copyright 2020 Alyssa Ross. All rights reserved.
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+@include /usr/share/policy/crosvm/common_device.policy
+
+accept4: 1
+# F_ADD_SEALS
+fcntl: arg1 == 1033
+ftruncate: 1
+memfd_create: 1
+open: return ENOENT
+openat: return ENOENT
+shutdown: 1
diff --git a/servers/Cargo.toml b/servers/Cargo.toml
new file mode 100644
index 0000000..c42c57f
--- /dev/null
+++ b/servers/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "servers"
+version = "0.1.0"
+authors = ["Alyssa Ross"]
+edition = "2018"
+
+[dependencies]
+io_jail = { path = "../io_jail" }
+libc = "*"
+msg_socket = { path = "../msg_socket" }
+sys_util = { path = "../sys_util" }
diff --git a/servers/src/jailed.rs b/servers/src/jailed.rs
new file mode 100644
index 0000000..c633efb
--- /dev/null
+++ b/servers/src/jailed.rs
@@ -0,0 +1,142 @@
+// Copyright 2020 Alyssa Ross. All rights reserved.
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::fmt::{self, Display, Formatter};
+use std::io;
+use std::os::unix::prelude::*;
+use std::time::Duration;
+
+use io_jail::Minijail;
+use libc;
+use msg_socket::{MsgError, MsgOnSocket, MsgReceiver, MsgSender, MsgSocket};
+use sys_util::net::UnixSeqpacket;
+use sys_util::{error, warn};
+
+use crate::Server;
+
+#[derive(Debug)]
+pub enum Error {
+    ForkingJail(io_jail::Error),
+    Io(io::Error),
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        use Error::*;
+        match self {
+            ForkingJail(e) => write!(f, "failed to fork jail process: {}", e),
+            Io(e) => write!(f, "IO error configuring proxy server: {}", e),
+        }
+    }
+}
+
+impl std::error::Error for Error {}
+
+const SOCKET_TIMEOUT_MS: u64 = 2000;
+
+#[derive(Copy, Clone, Debug, Eq, MsgOnSocket, PartialEq)]
+enum Event {
+    Activate,
+    Shutdown,
+}
+
+#[derive(Debug)]
+pub struct JailedServer<S> {
+    sock: MsgSocket<Event, ()>,
+    server: S,
+}
+
+fn recv_ack(sock: &MsgSocket<(), Event>) -> Result<Event, MsgError> {
+    let event = sock.recv()?;
+    sock.send(&())?;
+    Ok(event)
+}
+
+fn child_proc<S: Server>(sock: UnixSeqpacket, server: &mut S) -> Result<(), MsgError> {
+    let sock = MsgSocket::new(sock);
+
+    if recv_ack(&sock)? == Event::Activate {
+        server.activate();
+
+        while recv_ack(&sock)? == Event::Activate {
+            warn!("server already activated");
+        }
+    }
+
+    eprintln!("Received shutdown");
+
+    // Event::Shutdown received.  Time to stop.
+    Ok(())
+}
+
+impl<S: Server> JailedServer<S> {
+    pub fn jail(mut server: S, jail: &Minijail, mut keep_fds: Vec<RawFd>) -> Result<Self, Error> {
+        let (child_sock, parent_sock) = UnixSeqpacket::pair().map_err(Error::Io)?;
+
+        keep_fds.push(child_sock.as_raw_fd());
+        let pid = unsafe { jail.fork(Some(&keep_fds)) }.map_err(Error::ForkingJail)?;
+
+        if pid == 0 {
+            server.on_sandboxed();
+            if let Err(e) = child_proc(child_sock, &mut server) {
+                error!("child server process: {}", e);
+            }
+
+            // We're explicitly not using std::process::exit here to avoid the cleanup of
+            // stdout/stderr globals. This can cause cascading panics and SIGILL if a worker
+            // thread attempts to log to stderr after at_exit handlers have been run.
+            // TODO(crbug.com/992494): Remove this once device shutdown ordering is clearly
+            // defined (or when corresponding code for devices has been removed).
+            //
+            // exit() is trivially safe.
+            // ! Never returns
+            unsafe { libc::exit(0) };
+        }
+
+        parent_sock
+            .set_write_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))
+            .map_err(Error::Io)?;
+        parent_sock
+            .set_read_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))
+            .map_err(Error::Io)?;
+
+        Ok(Self {
+            server,
+            sock: MsgSocket::new(parent_sock),
+        })
+    }
+}
+
+impl<S> JailedServer<S> {
+    fn sync_send(&self, event: &Event) {
+        if let Err(e) = self.sock.send(event) {
+            error!("failed to send message to server: {}", e);
+        }
+
+        if let Err(e) = self.sock.recv() {
+            error!("failed to receive ack from server: {}", e);
+        }
+    }
+}
+
+impl<S> Drop for JailedServer<S> {
+    fn drop(&mut self) {
+        self.sync_send(&Event::Shutdown);
+    }
+}
+
+impl<S> Server for JailedServer<S> {
+    fn keep_fds(&self) -> Vec<RawFd> {
+        panic!("Don't double jail servers!");
+    }
+
+    fn on_sandboxed(&mut self) {
+        panic!("Don't double jail servers!");
+    }
+
+    fn activate(&mut self) {
+        self.sync_send(&Event::Activate);
+    }
+}
diff --git a/servers/src/lib.rs b/servers/src/lib.rs
new file mode 100644
index 0000000..5c174f9
--- /dev/null
+++ b/servers/src/lib.rs
@@ -0,0 +1,32 @@
+// Copyright 2020 Alyssa Ross. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+mod jailed;
+mod memfd;
+
+use std::os::unix::prelude::*;
+
+pub use jailed::Error as ProxyError;
+pub use jailed::JailedServer;
+pub use memfd::MemfdServer;
+
+pub trait Server {
+    fn keep_fds(&self) -> Vec<RawFd>;
+    fn on_sandboxed(&mut self) {}
+    fn activate(&mut self);
+}
+
+impl<T: Server + ?Sized> Server for Box<T> {
+    fn keep_fds(&self) -> Vec<RawFd> {
+        (**self).keep_fds()
+    }
+
+    fn on_sandboxed(&mut self) {
+        (**self).on_sandboxed()
+    }
+
+    fn activate(&mut self) {
+        (**self).activate()
+    }
+}
diff --git a/servers/src/memfd.rs b/servers/src/memfd.rs
new file mode 100644
index 0000000..ea6e148
--- /dev/null
+++ b/servers/src/memfd.rs
@@ -0,0 +1,171 @@
+// Copyright 2020 Alyssa Ross. All rights reserved.
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::convert::TryInto;
+use std::error::Error;
+use std::io::prelude::*;
+use std::io::IoSlice;
+use std::net::Shutdown;
+use std::os::unix::net::{UnixListener, UnixStream};
+use std::os::unix::prelude::*;
+use std::thread::{self, JoinHandle};
+
+use sys_util::{error, EventFd, MemfdSeals, PollContext, PollToken, ScmSocket, SharedMemory};
+
+use crate::Server;
+
+fn create_memfd(name: [u8; 224], size: u64) -> Result<SharedMemory, Box<dyn Error>> {
+    let mut full_name: Vec<u8> = b"crosvm-guest-memfd-".to_vec();
+    full_name.extend(
+        name.iter()
+            .map(|x| *x)
+            .take_while(|b| *b != 0 && *b != b'/'),
+    );
+
+    let size: usize = size.try_into()?;
+
+    let mut seals = MemfdSeals::new();
+    seals.set_grow_seal();
+    seals.set_shrink_seal();
+    seals.set_seal_seal();
+
+    let mut memfd = SharedMemory::named(full_name)?;
+    memfd.set_size(size as u64)?;
+    memfd.add_seals(seals)?;
+
+    Ok(memfd)
+}
+
+fn do_request(mut conn: UnixStream) -> Result<(), Box<dyn Error>> {
+    let mut name = [0; 224];
+    conn.read_exact(&mut name)?;
+
+    let mut size = [0; 8];
+    conn.read_exact(&mut size)?;
+    let size = u64::from_le_bytes(size);
+
+    let _ = conn.shutdown(Shutdown::Read);
+
+    match create_memfd(name, size) {
+        Ok(memfd) => conn.send_with_fd(&[IoSlice::new(&[0x00])], memfd.as_raw_fd())?,
+        Err(_) => conn.write(&[0x01])?,
+    };
+
+    Ok(())
+}
+
+fn run(wl_socket: UnixListener, kill_evt: EventFd) {
+    #[derive(Debug, PollToken)]
+    enum Token {
+        Socket,
+        Kill,
+    }
+
+    let poll_ctx =
+        match PollContext::build_with(&[(&wl_socket, Token::Socket), (&kill_evt, Token::Kill)]) {
+            Ok(pc) => pc,
+            Err(e) => {
+                error!("failed creating PollContext: {}", e);
+                return;
+            }
+        };
+
+    'poll: loop {
+        let events = match poll_ctx.wait() {
+            Ok(v) => v,
+            Err(e) => {
+                error!("failed polling for events: {}", e);
+                break;
+            }
+        };
+
+        for event in &events {
+            match dbg!(event.token()) {
+                Token::Socket => {
+                    let conn = match wl_socket.accept() {
+                        Ok((conn, _)) => conn,
+                        Err(e) => {
+                            error!("Failed to accept memfd connection: {}", e);
+                            break 'poll;
+                        }
+                    };
+
+                    if let Err(e) = do_request(conn) {
+                        error!("Failed to service memfd request: {}", e);
+                        break 'poll;
+                    }
+                }
+
+                Token::Kill => break 'poll,
+            }
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct MemfdServer {
+    kill_evt: Option<EventFd>,
+    worker_thread: Option<JoinHandle<()>>,
+    wl_socket: Option<UnixListener>,
+}
+
+impl MemfdServer {
+    pub fn new(wl_socket: UnixListener) -> Self {
+        Self {
+            kill_evt: None,
+            worker_thread: None,
+            wl_socket: Some(wl_socket),
+        }
+    }
+}
+
+impl Drop for MemfdServer {
+    fn drop(&mut self) {
+        if let Some(kill_evt) = self.kill_evt.take() {
+            // Ignore the result because there is nothing we can do about it.
+            let _ = kill_evt.write(1);
+        }
+
+        if let Some(worker_thread) = self.worker_thread.take() {
+            let _ = worker_thread.join();
+        }
+    }
+}
+
+impl Server for MemfdServer {
+    fn keep_fds(&self) -> Vec<RawFd> {
+        self.wl_socket.iter().map(AsRawFd::as_raw_fd).collect()
+    }
+
+    fn activate(&mut self) {
+        let wl_socket = match self.wl_socket.take() {
+            Some(wl) => wl,
+            None => return,
+        };
+
+        let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+            Ok(v) => v,
+            Err(e) => {
+                error!("failed creating kill EventFd pair: {}", e);
+                return;
+            }
+        };
+        self.kill_evt = Some(self_kill_evt);
+
+        let worker_result = thread::Builder::new()
+            .name("memfd-server".to_string())
+            .spawn(move || run(wl_socket, kill_evt));
+
+        match worker_result {
+            Err(e) => {
+                error!("failed to spawn memfd server worker: {}", e);
+                return;
+            }
+            Ok(join_handle) => {
+                self.worker_thread = Some(join_handle);
+            }
+        }
+    }
+}
diff --git a/src/crosvm.rs b/src/crosvm.rs
index e6e7268..ab4429a 100644
--- a/src/crosvm.rs
+++ b/src/crosvm.rs
@@ -180,6 +180,7 @@ pub struct Config {
     pub vhost_net: bool,
     pub tap_fd: Vec<RawFd>,
     pub cid: Option<u64>,
+    pub wl_memfd: bool,
     pub wayland_socket_paths: BTreeMap<String, PathBuf>,
     pub remote_wayland_device_socket_path: Option<PathBuf>,
     pub wayland_dmabuf: bool,
@@ -235,6 +236,7 @@ impl Default for Config {
             #[cfg(feature = "gpu")]
             gpu_parameters: None,
             software_tpm: false,
+            wl_memfd: false,
             wayland_socket_paths: BTreeMap::new(),
             remote_wayland_device_socket_path: None,
             wayland_dmabuf: false,
diff --git a/src/linux.rs b/src/linux.rs
index ee248a4..d5e12cd 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -5,8 +5,9 @@
 use std::cmp::max;
 use std::collections::{BTreeMap, VecDeque};
 use std::convert::TryFrom;
+use std::env;
 use std::error::Error as StdError;
-use std::ffi::CStr;
+use std::ffi::{CStr, OsString};
 use std::fmt::{self, Display};
 use std::fs::{File, OpenOptions};
 use std::io::{self, stdin, Read};
@@ -16,8 +17,9 @@ use std::net::Ipv4Addr;
 use std::num::NonZeroU8;
 use std::num::ParseIntError;
 use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
-use std::os::unix::net::UnixStream;
+use std::os::unix::net::{UnixListener, UnixStream};
 use std::path::{Path, PathBuf};
+use std::process;
 use std::ptr;
 use std::str;
 use std::sync::{Arc, Barrier};
@@ -42,6 +44,7 @@ use msg_socket::{MsgError, MsgReceiver, MsgResult, MsgSender, MsgSocket};
 use net_util::{Error as NetError, MacAddress, Tap};
 use remain::sorted;
 use resources::{Alloc, MmioType, SystemAllocator};
+use servers::MemfdServer;
 use sync::{Condvar, Mutex};
 use sys_util::net::{UnixSeqpacket, UnixSeqpacketListener, UnlinkUnixSeqpacketListener};
 
@@ -63,8 +66,8 @@ use vm_control::{
 
 use crate::{Config, DiskOption, Executable, SharedDir, SharedDirKind, TouchDeviceOption};
 use arch::{
-    self, LinuxArch, RunnableLinuxVm, SerialHardware, SerialParameters, VirtioDeviceStub,
-    VmComponents, VmImage,
+    self, LinuxArch, RunnableLinuxVm, SerialHardware, SerialParameters, ServerStub,
+    VirtioDeviceStub, VmComponents, VmImage,
 };
 
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
@@ -407,6 +410,16 @@ fn simple_jail(cfg: &Config, policy: &str) -> Result<Option<Minijail>> {
 }
 
 type DeviceResult<T = VirtioDeviceStub> = std::result::Result<T, Error>;
+type ServerResult<T = ServerStub> = std::result::Result<T, Error>;
+
+fn create_memfd_server(cfg: &Config, memfd_socket_path: &Path) -> ServerResult {
+    let sock = UnixListener::bind(&memfd_socket_path).map_err(Error::CreateSocket)?;
+    let server = MemfdServer::new(sock);
+    Ok(ServerStub {
+        server: Box::new(server),
+        jail: simple_jail(&cfg, "memfd_server")?,
+    })
+}
 
 fn create_block_device(
     cfg: &Config,
@@ -461,7 +474,6 @@ fn create_rng_device(cfg: &Config) -> DeviceResult {
 fn create_tpm_device(cfg: &Config) -> DeviceResult {
     use std::ffi::CString;
     use std::fs;
-    use std::process;
     use sys_util::chown;
 
     let tpm_storage: PathBuf;
@@ -756,12 +768,18 @@ fn create_gpu_device(
 
 fn create_wayland_device(
     cfg: &Config,
+    memfd_socket_path: Option<&Path>,
     vm_socket: VmMemoryControlRequestSocket,
     resource_bridge: Option<virtio::resource_bridge::ResourceRequestSocket>,
     memory_params: MemoryParams,
 ) -> DeviceResult {
-    let wayland_socket_dirs = cfg
-        .wayland_socket_paths
+    let mut wayland_socket_paths = cfg.wayland_socket_paths.clone();
+
+    if let Some(memfd_socket_path) = memfd_socket_path {
+        wayland_socket_paths.insert("__crosvm_memfd".to_string(), memfd_socket_path.into());
+    }
+
+    let wayland_socket_dirs = wayland_socket_paths
         .iter()
         .map(|(_name, path)| path.parent())
         .collect::<Option<Vec<_>>>()
@@ -778,7 +796,7 @@ fn create_wayland_device(
 
         None => Box::new(
             virtio::Wl::new(Params {
-                wayland_paths: cfg.wayland_socket_paths.clone(),
+                wayland_paths: wayland_socket_paths.clone(),
                 vm_socket,
                 resource_bridge,
             })
@@ -1076,6 +1094,7 @@ fn create_virtio_devices(
     vm: &mut Vm,
     resources: &mut SystemAllocator,
     _exit_evt: &EventFd,
+    memfd_socket_path: Option<&Path>,
     wayland_device_control_socket: VmMemoryControlRequestSocket,
     gpu_device_control_socket: VmMemoryControlRequestSocket,
     balloon_device_control_socket: BalloonControlResponseSocket,
@@ -1171,6 +1190,7 @@ fn create_virtio_devices(
 
         devs.push(create_wayland_device(
             cfg,
+            memfd_socket_path,
             wayland_device_control_socket,
             wl_resource_bridge,
             mem_params,
@@ -1279,6 +1299,7 @@ fn create_devices(
     vm: &mut Vm,
     resources: &mut SystemAllocator,
     exit_evt: &EventFd,
+    memfd_socket_path: Option<&Path>,
     control_sockets: &mut Vec<TaggedControlSocket>,
     wayland_device_control_socket: VmMemoryControlRequestSocket,
     gpu_device_control_socket: VmMemoryControlRequestSocket,
@@ -1294,6 +1315,7 @@ fn create_devices(
         vm,
         resources,
         exit_evt,
+        memfd_socket_path,
         wayland_device_control_socket,
         gpu_device_control_socket,
         balloon_device_control_socket,
@@ -1830,6 +1852,29 @@ pub fn run_config(cfg: Config) -> Result<()> {
         msg_socket::pair::<VmIrqResponse, VmIrqRequest>().map_err(Error::CreateSocket)?;
     control_sockets.push(TaggedControlSocket::VmIrq(ioapic_host_socket));
 
+    let mut servers = vec![];
+    let mut memfd_socket_path = None;
+
+    if cfg.wl_memfd {
+        let mut sock_name = OsString::from("crosvm-");
+        sock_name.push(process::id().to_string());
+        sock_name.push("-memfd.sock");
+
+        memfd_socket_path = Some(
+            [
+                env::var_os("XDG_RUNTIME_DIR").expect("XDG_RUNTIME_DIR not set"),
+                sock_name,
+            ]
+            .iter()
+            .collect::<PathBuf>(),
+        );
+
+        servers.push(create_memfd_server(
+            &cfg,
+            memfd_socket_path.as_ref().unwrap(),
+        )?);
+    }
+
     let sandbox = cfg.sandbox;
     let linux = Arch::build_vm(
         components,
@@ -1837,6 +1882,7 @@ pub fn run_config(cfg: Config) -> Result<()> {
         ioapic_device_control_socket,
         &cfg.serial_parameters,
         simple_jail(&cfg, "serial")?,
+        servers,
         |mem, mem_params, vm, sys_allocator, exit_evt| {
             create_devices(
                 &cfg,
@@ -1845,6 +1891,7 @@ pub fn run_config(cfg: Config) -> Result<()> {
                 vm,
                 sys_allocator,
                 exit_evt,
+                memfd_socket_path.as_ref().map(Path::new),
                 &mut control_sockets,
                 wayland_device_control_socket,
                 gpu_device_control_socket,
@@ -1952,6 +1999,10 @@ fn run_control(
         drop_capabilities().map_err(Error::DropCapabilities)?;
     }
 
+    for server in &mut linux.servers {
+        server.activate();
+    }
+
     let mut vcpu_handles = Vec::with_capacity(linux.vcpus.len());
     let vcpu_thread_barrier = Arc::new(Barrier::new(linux.vcpus.len() + 1));
     let run_mode_arc = Arc::new(VcpuRunMode::default());
diff --git a/src/main.rs b/src/main.rs
index 7ea2e01..87ecdb6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -829,7 +829,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                         })?,
                 )
         }
-
+        "wl-memfd" => cfg.wl_memfd = true,
         "wayland-sock" => {
             let mut components = value.unwrap().split(',');
             let path =
@@ -1469,6 +1469,10 @@ Number of virtio-net virtual queue pairs.
 (default: 1)
 ",
         ),
+        Argument::flag(
+            "wl-memfd",
+            "Enable support for requesting memfds over virtio_wl.",
+        ),
         Argument::value(
             "wayland-sock",
             "PATH[,name=NAME]",
diff --git a/x86_64/Cargo.toml b/x86_64/Cargo.toml
index 49ef53f..6f6bfaf 100644
--- a/x86_64/Cargo.toml
+++ b/x86_64/Cargo.toml
@@ -17,6 +17,7 @@ kvm_sys = { path = "../kvm_sys" }
 libc = "*"
 remain = "*"
 resources = { path = "../resources" }
+servers = { path = "../servers" }
 sync = { path = "../sync" }
 sys_util = { path = "../sys_util" }
 acpi_tables = {path = "../acpi_tables" }
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index 3aff53d..7c136ec 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -57,8 +57,8 @@ use crate::bootparam::boot_params;
 use acpi_tables::aml::Aml;
 use acpi_tables::sdt::SDT;
 use arch::{
-    get_serial_cmdline, GetSerialCmdlineError, RunnableLinuxVm, SerialHardware, SerialParameters,
-    VmComponents, VmImage,
+    get_serial_cmdline, jail_servers, GetSerialCmdlineError, RunnableLinuxVm, SerialHardware,
+    SerialParameters, ServerStub, VmComponents, VmImage,
 };
 use devices::split_irqchip_common::GsiRelay;
 use devices::{
@@ -97,6 +97,7 @@ pub enum Error {
     E820Configuration,
     EnableSplitIrqchip(sys_util::Error),
     GetSerialCmdline(GetSerialCmdlineError),
+    JailServers(servers::ProxyError),
     KernelOffsetPastEnd,
     LoadBios(io::Error),
     LoadBzImage(bzimage::Error),
@@ -148,6 +149,7 @@ impl Display for Error {
             E820Configuration => write!(f, "invalid e820 setup params"),
             EnableSplitIrqchip(e) => write!(f, "failed to enable split irqchip: {}", e),
             GetSerialCmdline(e) => write!(f, "failed to get serial cmdline: {}", e),
+            JailServers(e) => write!(f, "failed to jail servers: {}", e),
             KernelOffsetPastEnd => write!(f, "the kernel extends past the end of RAM"),
             LoadBios(e) => write!(f, "error loading bios: {}", e),
             LoadBzImage(e) => write!(f, "error loading kernel bzImage: {}", e),
@@ -347,6 +349,7 @@ impl arch::LinuxArch for X8664arch {
         ioapic_device_socket: VmIrqRequestSocket,
         serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
         serial_jail: Option<Minijail>,
+        servers: Vec<ServerStub>,
         create_devices: F,
     ) -> Result<RunnableLinuxVm>
     where
@@ -408,6 +411,9 @@ impl arch::LinuxArch for X8664arch {
         } else {
             (None, None)
         };
+
+        let servers = jail_servers(servers).map_err(Error::JailServers)?;
+
         let pci_devices = create_devices(&mem, mem_params, &mut vm, &mut resources, &exit_evt)
             .map_err(|e| Error::CreateDevices(Box::new(e)))?;
         let (pci, pci_irqs, pid_debug_label_map) = arch::generate_pci_root(
@@ -532,6 +538,7 @@ impl arch::LinuxArch for X8664arch {
             irq_chip,
             split_irqchip,
             gsi_relay,
+            servers,
             io_bus,
             mmio_bus,
             pid_debug_label_map,