diff options
-rw-r--r-- | devices/src/virtio/gpu/mod.rs | 16 | ||||
-rw-r--r-- | gpu_display/src/display_wl.c | 4 | ||||
-rw-r--r-- | gpu_display/src/dwl.rs | 66 | ||||
-rw-r--r-- | gpu_display/src/lib.rs | 28 | ||||
-rw-r--r-- | seccomp/x86_64/gpu_device.policy | 57 | ||||
-rw-r--r-- | src/linux.rs | 90 |
6 files changed, 215 insertions, 46 deletions
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs index 65c01c5..38efa41 100644 --- a/devices/src/virtio/gpu/mod.rs +++ b/devices/src/virtio/gpu/mod.rs @@ -13,7 +13,8 @@ use std::cell::RefCell; use std::collections::VecDeque; use std::i64; use std::mem::size_of; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::path::{Path, PathBuf}; use std::rc::Rc; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -587,14 +588,16 @@ pub struct Gpu { config_event: bool, exit_evt: EventFd, kill_evt: Option<EventFd>, + wayland_socket_path: PathBuf, } impl Gpu { - pub fn new(exit_evt: EventFd) -> Gpu { + pub fn new<P: AsRef<Path>>(exit_evt: EventFd, wayland_socket_path: P) -> Gpu { Gpu { config_event: false, exit_evt, kill_evt: None, + wayland_socket_path: wayland_socket_path.as_ref().to_path_buf(), } } @@ -623,7 +626,9 @@ impl Drop for Gpu { impl VirtioDevice for Gpu { fn keep_fds(&self) -> Vec<RawFd> { - Vec::new() + let mut keep_fds = Vec::new(); + keep_fds.push(self.exit_evt.as_raw_fd()); + keep_fds } fn device_type(&self) -> u32 { @@ -706,6 +711,7 @@ impl VirtioDevice for Gpu { let ctrl_evt = queue_evts.remove(0); let cursor_queue = queues.remove(0); let cursor_evt = queue_evts.remove(0); + let socket_path = self.wayland_socket_path.clone(); spawn(move || { const UNDESIRED_CARDS: &[&str] = &["vgem", "pvr"]; let drm_card = match gpu_buffer::rendernode::open_device(UNDESIRED_CARDS) { @@ -724,10 +730,10 @@ impl VirtioDevice for Gpu { } }; - let display = match GpuDisplay::new() { + let display = match GpuDisplay::new(socket_path) { Ok(c) => c, Err(e) => { - error!("{:?}", e); + error!("failed to open display: {:?}", e); return; } }; diff --git a/gpu_display/src/display_wl.c b/gpu_display/src/display_wl.c index e0c4e24..e308506 100644 --- a/gpu_display/src/display_wl.c +++ b/gpu_display/src/display_wl.c @@ -374,9 +374,9 @@ void dwl_context_destroy(struct dwl_context **self) *self = NULL; } -bool dwl_context_setup(struct dwl_context *self) +bool dwl_context_setup(struct dwl_context *self, const char *socket_path) { - struct wl_display *display = wl_display_connect(NULL); + struct wl_display *display = wl_display_connect(socket_path); if (!display) { printf("failed to connect to display\n"); return false; diff --git a/gpu_display/src/dwl.rs b/gpu_display/src/dwl.rs index 9101c82..cbe337d 100644 --- a/gpu_display/src/dwl.rs +++ b/gpu_display/src/dwl.rs @@ -1,19 +1,58 @@ /* automatically generated by rust-bindgen */ -# [ repr ( C ) ] -# [ derive ( Debug , Copy , Clone ) ] -pub struct dwl_context { + +/// @page page_xdg_shell_unstable_v6 The xdg_shell_unstable_v6 protocol +/// @section page_ifaces_xdg_shell_unstable_v6 Interfaces +/// - @subpage page_iface_zxdg_shell_v6 - create desktop-style surfaces +/// - @subpage page_iface_zxdg_positioner_v6 - child surface positioner +/// - @subpage page_iface_zxdg_surface_v6 - desktop user interface surface base interface +/// - @subpage page_iface_zxdg_toplevel_v6 - toplevel surface +/// - @subpage page_iface_zxdg_popup_v6 - short-lived, popup surfaces for menus +/// @section page_copyright_xdg_shell_unstable_v6 Copyright +/// <pre> +/// +/// Copyright © 2008-2013 Kristian Høgsberg +/// Copyright © 2013 Rafael Antognolli +/// Copyright © 2013 Jasper St. Pierre +/// Copyright © 2010-2013 Intel Corporation +/// +/// Permission is hereby granted, free of charge, to any person obtaining a +/// copy of this software and associated documentation files (the "Software"), +/// to deal in the Software without restriction, including without limitation +/// the rights to use, copy, modify, merge, publish, distribute, sublicense, +/// and/or sell copies of the Software, and to permit persons to whom the +/// Software is furnished to do so, subject to the following conditions: +/// +/// The above copyright notice and this permission notice (including the next +/// paragraph) shall be included in all copies or substantial portions of the +/// Software. +/// +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +/// DEALINGS IN THE SOFTWARE. +/// </pre> +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wl_output { _unused: [u8; 0], } -# [ repr ( C ) ] -# [ derive ( Debug , Copy , Clone ) ] +#[repr(C)] +pub struct dwl_context { + pub _bindgen_opaque_blob: [u64; 52usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct dwl_dmabuf { - _unused: [u8; 0], + pub _bindgen_opaque_blob: [u64; 3usize], } -# [ repr ( C ) ] -# [ derive ( Debug , Copy , Clone ) ] +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct dwl_surface { - _unused: [u8; 0], + pub _bindgen_opaque_blob: [u64; 12usize], } extern "C" { pub fn dwl_context_new() -> *mut dwl_context; @@ -22,7 +61,9 @@ extern "C" { pub fn dwl_context_destroy(self_: *mut *mut dwl_context); } extern "C" { - pub fn dwl_context_setup(self_: *mut dwl_context) -> bool; + pub fn dwl_context_setup(self_: *mut dwl_context, + socket_path: *const ::std::os::raw::c_char) + -> bool; } extern "C" { pub fn dwl_context_fd(self_: *mut dwl_context) -> ::std::os::raw::c_int; @@ -42,9 +83,6 @@ extern "C" { -> *mut dwl_dmabuf; } extern "C" { - pub fn dwl_dmabuf_in_use(self_: *mut dwl_dmabuf) -> bool; -} -extern "C" { pub fn dwl_dmabuf_destroy(self_: *mut *mut dwl_dmabuf); } extern "C" { @@ -78,4 +116,4 @@ extern "C" { } extern "C" { pub fn dwl_surface_set_position(self_: *mut dwl_surface, x: u32, y: u32); -} \ No newline at end of file +} diff --git a/gpu_display/src/lib.rs b/gpu_display/src/lib.rs index 7f2b775..7ff116a 100644 --- a/gpu_display/src/lib.rs +++ b/gpu_display/src/lib.rs @@ -11,8 +11,9 @@ mod dwl; use std::cell::Cell; use std::collections::HashMap; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use std::os::unix::io::{AsRawFd, RawFd}; +use std::path::Path; use std::ptr::null_mut; use data_model::{VolatileSlice, VolatileMemory}; @@ -40,6 +41,8 @@ pub enum GpuDisplayError { FailedImport, /// The surface ID is invalid. InvalidSurfaceId, + /// The path is invalid. + InvalidPath, } struct DwlContext(*mut dwl_context); @@ -108,15 +111,23 @@ pub struct GpuDisplay { impl GpuDisplay { /// Opens a fresh connection to the compositor. - pub fn new() -> Result<GpuDisplay, GpuDisplayError> { + pub fn new<P: AsRef<Path>>(wayland_path: P) -> Result<GpuDisplay, GpuDisplayError> { // The dwl_context_new call should always be safe to call, and we check its result. let ctx = DwlContext(unsafe { dwl_context_new() }); if ctx.0.is_null() { return Err(GpuDisplayError::Allocate); } + // The dwl_context_setup call is always safe to call given that the supplied context is // valid. and we check its result. - let setup_success = unsafe { dwl_context_setup(ctx.0) }; + let cstr_path = match wayland_path.as_ref().as_os_str().to_str() { + Some(str) => match CString::new(str) { + Ok(cstr) => cstr, + Err(_) => return Err(GpuDisplayError::InvalidPath), + }, + None => return Err(GpuDisplayError::InvalidPath), + }; + let setup_success = unsafe { dwl_context_setup(ctx.0, cstr_path.as_ptr() ) }; if !setup_success { return Err(GpuDisplayError::Connect); } @@ -171,17 +182,6 @@ impl GpuDisplay { Ok(next_id) } - pub fn import_in_use(&mut self, import_id: u32) -> bool { - match self.dmabufs.get(&import_id) { - // Safe because only a valid dmabuf is used. - Some(dmabuf) => unsafe { dwl_dmabuf_in_use(dmabuf.0) }, - None => { - debug_assert!(false, "invalid import_id {}", import_id); - false - } - } - } - /// Releases a previously imported dmabuf identified by the given handle. pub fn release_import(&mut self, import_id: u32) { self.dmabufs.remove(&import_id); diff --git a/seccomp/x86_64/gpu_device.policy b/seccomp/x86_64/gpu_device.policy new file mode 100644 index 0000000..de16d39 --- /dev/null +++ b/seccomp/x86_64/gpu_device.policy @@ -0,0 +1,57 @@ +# 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. + +open: 1 +close: 1 +sigaltstack: 1 +munmap: 1 +write: 1 +# Allow mmap to allow loading of GL shared libraries. +mmap: arg2 == PROT_READ|PROT_WRITE || arg2 == PROT_NONE || arg2 == PROT_READ|PROT_EXEC +restart_syscall: 1 +exit_group: 1 +rt_sigreturn: 1 +# Allow MADV_DONTDUMP only. +madvise: arg2 == MADV_DONTDUMP || arg2 == MADV_DONTNEED +# Used to determine shm size after recvmsg with fd. +lseek: 1 +mprotect: arg2 == PROT_READ|PROT_WRITE || arg2 == PROT_NONE || arg2 == PROT_READ +sched_getaffinity: 1 +set_robust_list: 1 +exit: 1 +getpid: 1 +recvfrom: 1 +dup: 1 +eventfd2: 1 +futex: 1 +# Disallow clone's other than new threads. +# arg0 is flags. Because kernel. +clone: arg0 & 0x00010000 +# arg1 == FIONBIO || arg1 == FIOCLEX || arg1 == DMA_BUF_IOCTL_SYNC || +# arg1 & DRM_IOCTL +ioctl: arg1 == FIONBIO || arg1 == FIOCLEX || arg1 == 0x40086200 || arg1 & 0x6400 +fstat: 1 +# Used to communicate with wayland. +recvmsg: 1 +sendmsg: 1 +poll: 1 +getrandom: 1 +read: 1 +geteuid: 1 +getuid: 1 +readlink: 1 +getdents: 1 +stat: 1 +epoll_create1: 1 +epoll_ctl: 1 +epoll_wait: 1 +# Used to connect to wayland. +# arg0 == AF_UNIX && arg1 == SOCK_STREAM|SOCK_CLOEXEC +socket: arg0 == 1 && arg1 == 0x80001 && arg2 == 0 +connect: 1 +# Used for sharing memory with wayland. arg1 == MFD_CLOEXEC|MFD_ALLOW_SEALING +memfd_create: arg1 == 3 +# Used to set of size new memfd. +ftruncate: 1 +fcntl: arg1 == F_DUPFD_CLOEXEC diff --git a/src/linux.rs b/src/linux.rs index a858129..a142d1d 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -441,17 +441,85 @@ fn create_virtio_devs(cfg: VirtIoDeviceInfo, #[cfg(feature = "gpu")] { if cfg.gpu { - let gpu_box = - Box::new(devices::virtio::Gpu::new(_exit_evt - .try_clone() - .map_err(Error::CloneEventFd)?)); - let jail = if cfg.multiprocess { - error!("jail for virtio-gpu is unimplemented"); - unimplemented!(); - } else { - None - }; - devs.push(VirtioDeviceStub {dev: gpu_box, jail}); + if let Some(wayland_socket_path) = cfg.wayland_socket_path.as_ref() { + let jailed_wayland_path = Path::new("/wayland-0"); + + let gpu_box = + Box::new(devices::virtio::Gpu::new(_exit_evt + .try_clone() + .map_err(Error::CloneEventFd)?, + if cfg.multiprocess { + &jailed_wayland_path + } else { + wayland_socket_path.as_path() + })); + + let jail = if cfg.multiprocess { + let policy_path: PathBuf = cfg.seccomp_policy_dir.join("gpu_device.policy"); + let mut jail = create_base_minijail(empty_root_path, &policy_path)?; + + // Create a tmpfs in the device's root directory so that we can bind mount the + // dri directory into it. The size=67108864 is size=64*1024*1024 or size=64MB. + jail.mount_with_data(Path::new("none"), Path::new("/"), "tmpfs", + (libc::MS_NOSUID | libc::MS_NODEV | + libc::MS_NOEXEC) as usize, + "size=67108864") + .unwrap(); + + // Device nodes required for DRM. + let sys_dev_char_path = Path::new("/sys/dev/char"); + jail.mount_bind(sys_dev_char_path, sys_dev_char_path, false) + .unwrap(); + let sys_devices_path = Path::new("/sys/devices"); + jail.mount_bind(sys_devices_path, sys_devices_path, false) + .unwrap(); + let drm_dri_path = Path::new("/dev/dri"); + jail.mount_bind(drm_dri_path, drm_dri_path, false) + .unwrap(); + + // Libraries that are required when mesa drivers are dynamically loaded. + let lib_path = Path::new("/lib64"); + jail.mount_bind(lib_path, lib_path, false) + .unwrap(); + let usr_lib_path = Path::new("/usr/lib64"); + jail.mount_bind(usr_lib_path, usr_lib_path, false) + .unwrap(); + + // Bind mount the wayland socket into jail's root. This is necessary since each + // new wayland context must open() the socket. + jail.mount_bind(wayland_socket_path.as_path(), jailed_wayland_path, true) + .unwrap(); + + // Set the uid/gid for the jailed process, and give a basic id map. This + // is required for the above bind mount to work. + let crosvm_user_group = CStr::from_bytes_with_nul(b"crosvm\0").unwrap(); + let crosvm_uid = match get_user_id(&crosvm_user_group) { + Ok(u) => u, + Err(e) => { + warn!("falling back to current user id for gpu: {:?}", e); + geteuid() + } + }; + let crosvm_gid = match get_group_id(&crosvm_user_group) { + Ok(u) => u, + Err(e) => { + warn!("falling back to current group id for gpu: {:?}", e); + getegid() + } + }; + jail.change_uid(crosvm_uid); + jail.change_gid(crosvm_gid); + jail.uidmap(&format!("{0} {0} 1", crosvm_uid)) + .map_err(Error::SettingUidMap)?; + jail.gidmap(&format!("{0} {0} 1", crosvm_gid)) + .map_err(Error::SettingGidMap)?; + + Some(jail) + } else { + None + }; + devs.push(VirtioDeviceStub {dev: gpu_box, jail}); + } } } |