• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::io;
6 use std::path::Path;
7 use std::path::PathBuf;
8 
9 use anyhow::bail;
10 use anyhow::Context;
11 use base::linux::max_open_files;
12 use base::RawDescriptor;
13 use cros_async::Executor;
14 use minijail::Minijail;
15 
16 use crate::virtio::vhost::user::device::fs::FsBackend;
17 use crate::virtio::vhost::user::device::fs::Options;
18 use crate::virtio::vhost::user::device::listener::sys::VhostUserListener;
19 use crate::virtio::vhost::user::device::listener::VhostUserListenerTrait;
20 
default_uidmap() -> String21 fn default_uidmap() -> String {
22     // SAFETY: trivially safe
23     let euid = unsafe { libc::geteuid() };
24     format!("{} {} 1", euid, euid)
25 }
26 
default_gidmap() -> String27 fn default_gidmap() -> String {
28     // SAFETY: trivially safe
29     let egid = unsafe { libc::getegid() };
30     format!("{} {} 1", egid, egid)
31 }
32 
jail_and_fork( mut keep_rds: Vec<RawDescriptor>, dir_path: PathBuf, uid: u32, gid: u32, uid_map: Option<String>, gid_map: Option<String>, ) -> anyhow::Result<i32>33 fn jail_and_fork(
34     mut keep_rds: Vec<RawDescriptor>,
35     dir_path: PathBuf,
36     uid: u32,
37     gid: u32,
38     uid_map: Option<String>,
39     gid_map: Option<String>,
40 ) -> anyhow::Result<i32> {
41     // Create new minijail sandbox
42     let mut j = Minijail::new()?;
43 
44     j.namespace_pids();
45     j.namespace_user();
46     j.namespace_user_disable_setgroups();
47     if uid != 0 {
48         j.change_uid(uid);
49     }
50     if gid != 0 {
51         j.change_gid(gid);
52     }
53     j.uidmap(&uid_map.unwrap_or_else(default_uidmap))?;
54     j.gidmap(&gid_map.unwrap_or_else(default_gidmap))?;
55     j.run_as_init();
56 
57     j.namespace_vfs();
58     j.namespace_net();
59     j.no_new_privs();
60 
61     // Only pivot_root if we are not re-using the current root directory.
62     if dir_path != Path::new("/") {
63         // It's safe to call `namespace_vfs` multiple times.
64         j.namespace_vfs();
65         j.enter_pivot_root(&dir_path)?;
66     }
67     j.set_remount_mode(libc::MS_SLAVE);
68 
69     let limit = max_open_files().context("failed to get max open files")?;
70     j.set_rlimit(libc::RLIMIT_NOFILE as i32, limit, limit)?;
71     // vvu locks around 512k memory. Just give 1M.
72     j.set_rlimit(libc::RLIMIT_MEMLOCK as i32, 1 << 20, 1 << 20)?;
73 
74     // Make sure there are no duplicates in keep_rds
75     keep_rds.sort_unstable();
76     keep_rds.dedup();
77 
78     // fork on the jail here
79     // SAFETY: trivially safe
80     let pid = unsafe { j.fork(Some(&keep_rds))? };
81 
82     if pid > 0 {
83         // Current FS driver jail does not use seccomp and jail_and_fork() does not have other
84         // users, so we do nothing here for seccomp_trace
85         // SAFETY: trivially safe
86         unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM) };
87     }
88 
89     if pid < 0 {
90         bail!("Fork error! {}", std::io::Error::last_os_error());
91     }
92 
93     Ok(pid)
94 }
95 
96 /// Starts a vhost-user fs device.
97 /// Returns an error if the given `args` is invalid or the device fails to run.
start_device(opts: Options) -> anyhow::Result<()>98 pub fn start_device(opts: Options) -> anyhow::Result<()> {
99     let ex = Executor::new().context("Failed to create executor")?;
100     let fs_device = FsBackend::new(&ex, &opts.tag, opts.cfg)?;
101 
102     let mut keep_rds = fs_device.keep_rds.clone();
103     let listener = VhostUserListener::new_socket(&opts.socket, Some(&mut keep_rds))?;
104 
105     base::syslog::push_descriptors(&mut keep_rds);
106     cros_tracing::push_descriptors!(&mut keep_rds);
107     metrics::push_descriptors(&mut keep_rds);
108 
109     let pid = jail_and_fork(
110         keep_rds,
111         opts.shared_dir,
112         opts.uid,
113         opts.gid,
114         opts.uid_map,
115         opts.gid_map,
116     )?;
117 
118     // Parent, nothing to do but wait and then exit
119     if pid != 0 {
120         // SAFETY: trivially safe
121         unsafe { libc::waitpid(pid, std::ptr::null_mut(), 0) };
122         return Ok(());
123     }
124 
125     // We need to set the no setuid fixup secure bit so that we don't drop capabilities when
126     // changing the thread uid/gid. Without this, creating new entries can fail in some corner
127     // cases.
128     const SECBIT_NO_SETUID_FIXUP: i32 = 1 << 2;
129 
130     // TODO(crbug.com/1199487): Remove this once libc provides the wrapper for all targets.
131     #[cfg(target_os = "linux")]
132     {
133         // SAFETY:
134         // Safe because this doesn't modify any memory and we check the return value.
135         let mut securebits = unsafe { libc::prctl(libc::PR_GET_SECUREBITS) };
136         if securebits < 0 {
137             bail!(io::Error::last_os_error());
138         }
139         securebits |= SECBIT_NO_SETUID_FIXUP;
140         // SAFETY:
141         // Safe because this doesn't modify any memory and we check the return value.
142         let ret = unsafe { libc::prctl(libc::PR_SET_SECUREBITS, securebits) };
143         if ret < 0 {
144             bail!(io::Error::last_os_error());
145         }
146     }
147 
148     // run_until() returns an Result<Result<..>> which the ? operator lets us flatten.
149     ex.run_until(listener.run_backend(fs_device, &ex))?
150 }
151