1818
1919pub mod security;
2020
21- use crate :: { framed_recv_blocking, WorkerHandshake , LOG_TARGET } ;
21+ use crate :: { framed_recv_blocking, SecurityStatus , WorkerHandshake , LOG_TARGET } ;
2222use cpu_time:: ProcessTime ;
2323use futures:: never:: Never ;
2424use parity_scale_codec:: Decode ;
2525use std:: {
2626 any:: Any ,
27- fmt, io,
28- os:: unix:: net:: UnixStream ,
27+ fmt:: { self } ,
28+ fs:: File ,
29+ io:: { self , Read , Write } ,
30+ os:: {
31+ fd:: { AsRawFd , FromRawFd , RawFd } ,
32+ unix:: net:: UnixStream ,
33+ } ,
2934 path:: PathBuf ,
3035 sync:: mpsc:: { Receiver , RecvTimeoutError } ,
3136 time:: Duration ,
@@ -78,7 +83,7 @@ macro_rules! decl_worker_main {
7883
7984 "--check-can-enable-landlock" => {
8085 #[ cfg( target_os = "linux" ) ]
81- let status = if let Err ( err) = security:: landlock:: check_is_fully_enabled ( ) {
86+ let status = if let Err ( err) = security:: landlock:: check_can_fully_enable ( ) {
8287 // Write the error to stderr, log it on the host-side.
8388 eprintln!( "{}" , err) ;
8489 -1
@@ -91,7 +96,7 @@ macro_rules! decl_worker_main {
9196 } ,
9297 "--check-can-enable-seccomp" => {
9398 #[ cfg( all( target_os = "linux" , target_arch = "x86_64" ) ) ]
94- let status = if let Err ( err) = security:: seccomp:: check_is_fully_enabled ( ) {
99+ let status = if let Err ( err) = security:: seccomp:: check_can_fully_enable ( ) {
95100 // Write the error to stderr, log it on the host-side.
96101 eprintln!( "{}" , err) ;
97102 -1
@@ -107,7 +112,7 @@ macro_rules! decl_worker_main {
107112 let cache_path_tempdir = std:: path:: Path :: new( & args[ 2 ] ) ;
108113 #[ cfg( target_os = "linux" ) ]
109114 let status = if let Err ( err) =
110- security:: change_root:: check_is_fully_enabled ( & cache_path_tempdir)
115+ security:: change_root:: check_can_fully_enable ( & cache_path_tempdir)
111116 {
112117 // Write the error to stderr, log it on the host-side.
113118 eprintln!( "{}" , err) ;
@@ -119,6 +124,21 @@ macro_rules! decl_worker_main {
119124 let status = -1 ;
120125 std:: process:: exit( status)
121126 } ,
127+ "--check-can-do-secure-clone" => {
128+ #[ cfg( target_os = "linux" ) ]
129+ // SAFETY: new process is spawned within a single threaded process. This
130+ // invariant is enforced by tests.
131+ let status = if let Err ( err) = unsafe { security:: clone:: check_can_fully_clone( ) } {
132+ // Write the error to stderr, log it on the host-side.
133+ eprintln!( "{}" , err) ;
134+ -1
135+ } else {
136+ 0
137+ } ;
138+ #[ cfg( not( target_os = "linux" ) ) ]
139+ let status = -1 ;
140+ std:: process:: exit( status)
141+ } ,
122142
123143 "test-sleep" => {
124144 std:: thread:: sleep( std:: time:: Duration :: from_secs( 5 ) ) ;
@@ -171,6 +191,84 @@ macro_rules! decl_worker_main {
171191 } ;
172192}
173193
194+ //taken from the os_pipe crate. Copied here to reduce one dependency and
195+ // because its type-safe abstractions do not play well with nix's clone
196+ #[ cfg( not( target_os = "macos" ) ) ]
197+ pub fn pipe2_cloexec ( ) -> io:: Result < ( libc:: c_int , libc:: c_int ) > {
198+ let mut fds: [ libc:: c_int ; 2 ] = [ 0 ; 2 ] ;
199+ let res = unsafe { libc:: pipe2 ( fds. as_mut_ptr ( ) , libc:: O_CLOEXEC ) } ;
200+ if res != 0 {
201+ return Err ( io:: Error :: last_os_error ( ) )
202+ }
203+ Ok ( ( fds[ 0 ] , fds[ 1 ] ) )
204+ }
205+
206+ #[ cfg( target_os = "macos" ) ]
207+ pub fn pipe2_cloexec ( ) -> io:: Result < ( libc:: c_int , libc:: c_int ) > {
208+ let mut fds: [ libc:: c_int ; 2 ] = [ 0 ; 2 ] ;
209+ let res = unsafe { libc:: pipe ( fds. as_mut_ptr ( ) ) } ;
210+ if res != 0 {
211+ return Err ( io:: Error :: last_os_error ( ) )
212+ }
213+ let res = unsafe { libc:: fcntl ( fds[ 0 ] , libc:: F_SETFD , libc:: FD_CLOEXEC ) } ;
214+ if res != 0 {
215+ return Err ( io:: Error :: last_os_error ( ) )
216+ }
217+ let res = unsafe { libc:: fcntl ( fds[ 1 ] , libc:: F_SETFD , libc:: FD_CLOEXEC ) } ;
218+ if res != 0 {
219+ return Err ( io:: Error :: last_os_error ( ) )
220+ }
221+ Ok ( ( fds[ 0 ] , fds[ 1 ] ) )
222+ }
223+
224+ /// A wrapper around a file descriptor used to encapsulate and restrict
225+ /// functionality for pipe operations.
226+ pub struct PipeFd {
227+ file : File ,
228+ }
229+
230+ impl AsRawFd for PipeFd {
231+ /// Returns the raw file descriptor associated with this `PipeFd`
232+ fn as_raw_fd ( & self ) -> RawFd {
233+ self . file . as_raw_fd ( )
234+ }
235+ }
236+
237+ impl FromRawFd for PipeFd {
238+ /// Creates a new `PipeFd` instance from a raw file descriptor.
239+ ///
240+ /// # Safety
241+ ///
242+ /// The fd passed in must be an owned file descriptor; in particular, it must be open.
243+ unsafe fn from_raw_fd ( fd : RawFd ) -> Self {
244+ PipeFd { file : File :: from_raw_fd ( fd) }
245+ }
246+ }
247+
248+ impl Read for PipeFd {
249+ fn read ( & mut self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
250+ self . file . read ( buf)
251+ }
252+
253+ fn read_to_end ( & mut self , buf : & mut Vec < u8 > ) -> io:: Result < usize > {
254+ self . file . read_to_end ( buf)
255+ }
256+ }
257+
258+ impl Write for PipeFd {
259+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
260+ self . file . write ( buf)
261+ }
262+
263+ fn flush ( & mut self ) -> io:: Result < ( ) > {
264+ self . file . flush ( )
265+ }
266+
267+ fn write_all ( & mut self , buf : & [ u8 ] ) -> io:: Result < ( ) > {
268+ self . file . write_all ( buf)
269+ }
270+ }
271+
174272/// Some allowed overhead that we account for in the "CPU time monitor" thread's sleeps, on the
175273/// child process.
176274pub const JOB_TIMEOUT_OVERHEAD : Duration = Duration :: from_millis ( 50 ) ;
@@ -192,14 +290,12 @@ impl fmt::Display for WorkerKind {
192290 }
193291}
194292
195- // Some fields are only used for logging, and dead-code analysis ignores Debug.
196- #[ allow( dead_code) ]
197293#[ derive( Debug ) ]
198294pub struct WorkerInfo {
199- pid : u32 ,
200- kind : WorkerKind ,
201- version : Option < String > ,
202- worker_dir_path : PathBuf ,
295+ pub pid : u32 ,
296+ pub kind : WorkerKind ,
297+ pub version : Option < String > ,
298+ pub worker_dir_path : PathBuf ,
203299}
204300
205301// NOTE: The worker version must be passed in so that we accurately get the version of the worker,
@@ -218,7 +314,7 @@ pub fn run_worker<F>(
218314 worker_version : Option < & str > ,
219315 mut event_loop : F ,
220316) where
221- F : FnMut ( UnixStream , PathBuf ) -> io:: Result < Never > ,
317+ F : FnMut ( UnixStream , & WorkerInfo , SecurityStatus ) -> io:: Result < Never > ,
222318{
223319 #[ cfg_attr( not( target_os = "linux" ) , allow( unused_mut) ) ]
224320 let mut worker_info = WorkerInfo {
@@ -250,11 +346,8 @@ pub fn run_worker<F>(
250346 }
251347
252348 // Make sure that we can read the worker dir path, and log its contents.
253- let entries = || -> Result < Vec < _ > , io:: Error > {
254- std:: fs:: read_dir ( & worker_info. worker_dir_path ) ?
255- . map ( |res| res. map ( |e| e. file_name ( ) ) )
256- . collect ( )
257- } ( ) ;
349+ let entries: io:: Result < Vec < _ > > = std:: fs:: read_dir ( & worker_info. worker_dir_path )
350+ . and_then ( |d| d. map ( |res| res. map ( |e| e. file_name ( ) ) ) . collect ( ) ) ;
258351 match entries {
259352 Ok ( entries) =>
260353 gum:: trace!( target: LOG_TARGET , ?worker_info, "content of worker dir: {:?}" , entries) ,
@@ -284,6 +377,22 @@ pub fn run_worker<F>(
284377 {
285378 gum:: trace!( target: LOG_TARGET , ?security_status, "Enabling security features" ) ;
286379
380+ // First, make sure env vars were cleared, to match the environment we perform the checks
381+ // within. (In theory, running checks with different env vars could result in different
382+ // outcomes of the checks.)
383+ if !security:: check_env_vars_were_cleared ( & worker_info) {
384+ let err = "not all env vars were cleared when spawning the process" ;
385+ gum:: error!(
386+ target: LOG_TARGET ,
387+ ?worker_info,
388+ "{}" ,
389+ err
390+ ) ;
391+ if security_status. secure_validator_mode {
392+ worker_shutdown ( worker_info, err) ;
393+ }
394+ }
395+
287396 // Call based on whether we can change root. Error out if it should work but fails.
288397 //
289398 // NOTE: This should not be called in a multi-threaded context (i.e. inside the tokio
@@ -337,23 +446,10 @@ pub fn run_worker<F>(
337446 }
338447 }
339448 }
340-
341- if !security:: check_env_vars_were_cleared ( & worker_info) {
342- let err = "not all env vars were cleared when spawning the process" ;
343- gum:: error!(
344- target: LOG_TARGET ,
345- ?worker_info,
346- "{}" ,
347- err
348- ) ;
349- if security_status. secure_validator_mode {
350- worker_shutdown ( worker_info, err) ;
351- }
352- }
353449 }
354450
355451 // Run the main worker loop.
356- let err = event_loop ( stream, worker_info. worker_dir_path . clone ( ) )
452+ let err = event_loop ( stream, & worker_info, security_status )
357453 // It's never `Ok` because it's `Ok(Never)`.
358454 . unwrap_err ( ) ;
359455
0 commit comments