@@ -120,16 +120,7 @@ impl Env {
120120 let exec_file = arguments
121121 . single_value ( "exec-file" )
122122 . ok_or_else ( || Error :: ArgumentParsing ( MissingValue ( "exec-file" . to_string ( ) ) ) ) ?;
123- let exec_file_path = canonicalize ( & exec_file)
124- . map_err ( |err| Error :: Canonicalize ( PathBuf :: from ( & exec_file) , err) ) ?;
125-
126- if !exec_file_path. is_file ( ) {
127- return Err ( Error :: NotAFile ( exec_file_path) ) ;
128- }
129-
130- let exec_file_name = exec_file_path
131- . file_name ( )
132- . ok_or_else ( || Error :: FileName ( exec_file_path. clone ( ) ) ) ?;
123+ let ( exec_file_path, exec_file_name) = Env :: validate_exec_file ( exec_file) ?;
133124
134125 let chroot_base = arguments
135126 . single_value ( "chroot-base-dir" )
@@ -169,7 +160,7 @@ impl Env {
169160 let mut cgroups: Vec < Box < dyn Cgroup > > = Vec :: new ( ) ;
170161 let parent_cgroup = match arguments. single_value ( "parent-cgroup" ) {
171162 Some ( parent_cg) => Path :: new ( parent_cg) ,
172- None => Path :: new ( exec_file_name) ,
163+ None => Path :: new ( & exec_file_name) ,
173164 } ;
174165 if parent_cgroup
175166 . components ( )
@@ -247,6 +238,29 @@ impl Env {
247238 self . uid
248239 }
249240
241+ fn validate_exec_file ( exec_file : & str ) -> Result < ( PathBuf , String ) > {
242+ let exec_file_path = canonicalize ( exec_file)
243+ . map_err ( |err| Error :: Canonicalize ( PathBuf :: from ( exec_file) , err) ) ?;
244+
245+ if !exec_file_path. is_file ( ) {
246+ return Err ( Error :: NotAFile ( exec_file_path) ) ;
247+ }
248+
249+ let exec_file_name = exec_file_path
250+ . file_name ( )
251+ . ok_or_else ( || Error :: ExtractFileName ( exec_file_path. clone ( ) ) ) ?
252+ . to_str ( )
253+ // Safe to unwrap as the original `exec_file` is `String`.
254+ . unwrap ( )
255+ . to_string ( ) ;
256+
257+ if !exec_file_name. contains ( "firecracker" ) {
258+ return Err ( Error :: ExecFileName ( exec_file_name) ) ;
259+ }
260+
261+ Ok ( ( exec_file_path, exec_file_name) )
262+ }
263+
250264 fn parse_resource_limits ( resource_limits : & mut ResourceLimits , args : & [ String ] ) -> Result < ( ) > {
251265 for arg in args {
252266 let ( name, value) = arg
@@ -293,7 +307,7 @@ impl Env {
293307 fn save_exec_file_pid ( & mut self , pid : i32 , chroot_exec_file : PathBuf ) -> Result < ( ) > {
294308 let chroot_exec_file_str = chroot_exec_file
295309 . to_str ( )
296- . ok_or_else ( || Error :: FileName ( chroot_exec_file. clone ( ) ) ) ?;
310+ . ok_or_else ( || Error :: ExtractFileName ( chroot_exec_file. clone ( ) ) ) ?;
297311 let pid_file_path =
298312 PathBuf :: from ( format ! ( "{}{}" , chroot_exec_file_str, PID_FILE_EXTENSION ) ) ;
299313 let mut pid_file = OpenOptions :: new ( )
@@ -363,7 +377,7 @@ impl Env {
363377 let exec_file_name = self
364378 . exec_file_path
365379 . file_name ( )
366- . ok_or_else ( || Error :: FileName ( self . exec_file_path . clone ( ) ) ) ?;
380+ . ok_or_else ( || Error :: ExtractFileName ( self . exec_file_path . clone ( ) ) ) ?;
367381 // We do a quick push here to get the global path of the executable inside the chroot,
368382 // without having to create a new PathBuf. We'll then do a pop to revert to the actual
369383 // chroot_dir right after the copy.
@@ -620,6 +634,8 @@ mod tests {
620634 use crate :: build_arg_parser;
621635 use crate :: cgroup:: test_util:: MockCgroupFs ;
622636
637+ const PSEUDO_EXEC_FILE_PATH : & str = "/tmp/pseudo_firecracker_exec_file" ;
638+
623639 #[ derive( Clone ) ]
624640 struct ArgVals < ' a > {
625641 pub id : & ' a str ,
@@ -637,9 +653,10 @@ mod tests {
637653
638654 impl ArgVals < ' _ > {
639655 pub fn new ( ) -> ArgVals < ' static > {
656+ File :: create ( PSEUDO_EXEC_FILE_PATH ) . unwrap ( ) ;
640657 ArgVals {
641658 id : "bd65600d-8669-4903-8a14-af88203add38" ,
642- exec_file : "/proc/cpuinfo" ,
659+ exec_file : PSEUDO_EXEC_FILE_PATH ,
643660 uid : "1001" ,
644661 gid : "1002" ,
645662 chroot_base : "/" ,
@@ -876,6 +893,49 @@ mod tests {
876893 }
877894 }
878895
896+ #[ test]
897+ fn test_validate_exec_file ( ) {
898+ // Success case
899+ File :: create ( PSEUDO_EXEC_FILE_PATH ) . unwrap ( ) ;
900+ assert ! ( Env :: validate_exec_file( PSEUDO_EXEC_FILE_PATH ) . is_ok( ) ) ;
901+
902+ // Error case 1: No such file exists
903+ std:: fs:: remove_file ( PSEUDO_EXEC_FILE_PATH ) . unwrap ( ) ;
904+ assert_eq ! (
905+ format!(
906+ "{}" ,
907+ Env :: validate_exec_file( PSEUDO_EXEC_FILE_PATH ) . unwrap_err( )
908+ ) ,
909+ format!(
910+ "Failed to canonicalize path {}: No such file or directory (os error 2)" ,
911+ PSEUDO_EXEC_FILE_PATH
912+ )
913+ ) ;
914+
915+ // Error case 2: Not a file
916+ std:: fs:: create_dir_all ( "/tmp/firecracker_test_dir" ) . unwrap ( ) ;
917+ assert_eq ! (
918+ format!(
919+ "{}" ,
920+ Env :: validate_exec_file( "/tmp/firecracker_test_dir" ) . unwrap_err( )
921+ ) ,
922+ "/tmp/firecracker_test_dir is not a file"
923+ ) ;
924+
925+ // Error case 3: Filename without "firecracker"
926+ File :: create ( "/tmp/firecracker_test_dir/foobarbaz" ) . unwrap ( ) ;
927+ assert_eq ! (
928+ format!(
929+ "{}" ,
930+ Env :: validate_exec_file( "/tmp/firecracker_test_dir/foobarbaz" ) . unwrap_err( )
931+ ) ,
932+ "Invalid filename. The filename of `--exec-file` option must contain \" firecracker\" : \
933+ foobarbaz"
934+ ) ;
935+ std:: fs:: remove_file ( "/tmp/firecracker_test_dir/foobarbaz" ) . unwrap ( ) ;
936+ std:: fs:: remove_dir_all ( "/tmp/firecracker_test_dir" ) . unwrap ( ) ;
937+ }
938+
879939 #[ test]
880940 fn test_setup_jailed_folder ( ) {
881941 let mut mock_cgroups = MockCgroupFs :: new ( ) . unwrap ( ) ;
@@ -986,15 +1046,15 @@ mod tests {
9861046 assert ! ( !mock_cgroups. add_v1_mounts( ) . is_err( ) ) ;
9871047
9881048 // Create tmp resources for `exec_file` and `chroot_base`.
989- let some_file = TempFile :: new_with_prefix ( "/tmp/" ) . unwrap ( ) ;
990- let some_file_path = some_file . as_path ( ) . to_str ( ) . unwrap ( ) ;
991- let some_file_name = some_file . as_path ( ) . file_name ( ) . unwrap ( ) ;
1049+ File :: create ( PSEUDO_EXEC_FILE_PATH ) . unwrap ( ) ;
1050+ let exec_file_path = PSEUDO_EXEC_FILE_PATH ;
1051+ let exec_file_name = Path :: new ( exec_file_path ) . file_name ( ) . unwrap ( ) ;
9921052 let some_dir = TempDir :: new ( ) . unwrap ( ) ;
9931053 let some_dir_path = some_dir. as_path ( ) . to_str ( ) . unwrap ( ) ;
9941054
9951055 let some_arg_vals = ArgVals {
9961056 id : "bd65600d-8669-4903-8a14-af88203add38" ,
997- exec_file : some_file_path ,
1057+ exec_file : exec_file_path ,
9981058 uid : "1001" ,
9991059 gid : "1002" ,
10001060 chroot_base : some_dir_path,
@@ -1005,7 +1065,7 @@ mod tests {
10051065 resource_limits : Vec :: new ( ) ,
10061066 parent_cgroup : None ,
10071067 } ;
1008- fs:: write ( some_file_path , "some_content" ) . unwrap ( ) ;
1068+ fs:: write ( exec_file_path , "some_content" ) . unwrap ( ) ;
10091069 args. parse ( & make_args ( & some_arg_vals) ) . unwrap ( ) ;
10101070 let mut env = Env :: new ( & args, 0 , 0 ) . unwrap ( ) ;
10111071
@@ -1014,10 +1074,10 @@ mod tests {
10141074
10151075 assert_eq ! (
10161076 env. copy_exec_to_chroot( ) . unwrap( ) ,
1017- some_file_name . to_os_string( )
1077+ exec_file_name . to_os_string( )
10181078 ) ;
10191079
1020- let dest_path = env. chroot_dir . join ( some_file_name ) ;
1080+ let dest_path = env. chroot_dir . join ( exec_file_name ) ;
10211081 // Check that `fs::copy()` copied src content and permission bits to destination.
10221082 let metadata_src = fs:: metadata ( & env. exec_file_path ) . unwrap ( ) ;
10231083 let metadata_dest = fs:: metadata ( & dest_path) . unwrap ( ) ;
0 commit comments