@@ -5,7 +5,6 @@ use crate::ffi::OsString;
55use crate :: fmt;
66use crate :: iter:: Iterator ;
77use crate :: mem:: size_of;
8- use crate :: os:: uefi:: ffi:: OsStringExt ;
98use crate :: sys:: uefi:: helpers;
109use crate :: vec;
1110
@@ -14,34 +13,32 @@ pub struct Args {
1413}
1514
1615pub fn args ( ) -> Args {
17- // SAFETY: Each loaded image has an image handle that supports EFI_LOADED_IMAGE_PROTOCOL
16+ let lazy_current_exe = || Vec :: from ( [ current_exe ( ) . map ( Into :: into) . unwrap_or_default ( ) ] ) ;
17+
18+ // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this
19+ // will never fail.
1820 let protocol =
19- helpers:: current_handle_protocol :: < loaded_image:: Protocol > ( loaded_image:: PROTOCOL_GUID )
21+ helpers:: image_handle_protocol :: < loaded_image:: Protocol > ( loaded_image:: PROTOCOL_GUID )
2022 . unwrap ( ) ;
2123
2224 let lp_size = unsafe { ( * protocol. as_ptr ( ) ) . load_options_size } as usize ;
23- let lp_size: usize = lp_size / size_of :: < u16 > ( ) ;
24-
25- if lp_size <= 0 {
26- return Args {
27- parsed_args_list : Vec :: from ( [ current_exe ( ) . map ( Into :: into) . unwrap_or_default ( ) ] )
28- . into_iter ( ) ,
29- } ;
25+ // Break if we are sure that it cannot be UTF-16
26+ if lp_size < size_of :: < u16 > ( ) || lp_size % size_of :: < u16 > ( ) != 0 {
27+ return Args { parsed_args_list : lazy_current_exe ( ) . into_iter ( ) } ;
3028 }
29+ let lp_size = lp_size / size_of :: < u16 > ( ) ;
3130
32- let lp_cmd_line = unsafe {
33- let temp = ( * protocol. as_ptr ( ) ) . load_options as * const u16 ;
34- crate :: slice:: from_raw_parts ( temp, lp_size)
35- } ;
36-
37- let vec_args = parse_lp_cmd_line ( lp_cmd_line) ;
38- let vec_args = if vec_args. is_empty ( ) {
39- Vec :: from ( [ current_exe ( ) . map ( Into :: into) . unwrap_or_default ( ) ] )
40- } else {
41- vec_args
42- } ;
31+ let lp_cmd_line = unsafe { ( * protocol. as_ptr ( ) ) . load_options as * const u16 } ;
32+ if !lp_cmd_line. is_aligned ( ) {
33+ return Args { parsed_args_list : lazy_current_exe ( ) . into_iter ( ) } ;
34+ }
35+ let lp_cmd_line = unsafe { crate :: slice:: from_raw_parts ( lp_cmd_line, lp_size) } ;
4336
44- Args { parsed_args_list : vec_args. into_iter ( ) }
37+ Args {
38+ parsed_args_list : parse_lp_cmd_line ( lp_cmd_line)
39+ . unwrap_or_else ( lazy_current_exe)
40+ . into_iter ( ) ,
41+ }
4542}
4643
4744impl fmt:: Debug for Args {
@@ -78,20 +75,25 @@ impl DoubleEndedIterator for Args {
7875///
7976/// This implementation is based on what is defined in Section 3.4 of
8077/// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_Spec_2_0.pdf)
81- fn parse_lp_cmd_line ( code_units : & [ u16 ] ) -> Vec < OsString > {
82- const QUOTE : u16 = b'"' as u16 ;
83- const SPACE : u16 = b' ' as u16 ;
84- const CARET : u16 = b'^' as u16 ;
85- const NULL : u16 = 0 ;
78+ ///
79+ /// Return None in the following cases:
80+ /// - Invalid UTF-16 (unpaired surrogate)
81+ /// - Empty/improper arguments
82+ fn parse_lp_cmd_line ( code_units : & [ u16 ] ) -> Option < Vec < OsString > > {
83+ const QUOTE : char = '"' ;
84+ const SPACE : char = ' ' ;
85+ const CARET : char = '^' ;
86+ const NULL : char = '\0' ;
8687
8788 let mut ret_val = Vec :: new ( ) ;
88- let mut code_units_iter = code_units. iter ( ) . peekable ( ) ;
89+ let mut code_units_iter = char :: decode_utf16 ( code_units. iter ( ) . cloned ( ) ) . peekable ( ) ;
8990
9091 // The executable name at the beginning is special.
9192 let mut in_quotes = false ;
92- let mut cur = Vec :: new ( ) ;
93+ let mut cur = String :: new ( ) ;
9394 while let Some ( w) = code_units_iter. next ( ) {
94- match * w {
95+ let w = w. ok ( ) ?;
96+ match w {
9597 // break on NULL
9698 NULL => break ,
9799 // A quote mark always toggles `in_quotes` no matter what because
@@ -100,13 +102,18 @@ fn parse_lp_cmd_line(code_units: &[u16]) -> Vec<OsString> {
100102 // If not `in_quotes` then whitespace ends argv[0].
101103 SPACE if !in_quotes => break ,
102104 // In all other cases the code unit is taken literally.
103- _ => cur. push ( * w) ,
105+ _ => cur. push ( w) ,
104106 }
105107 }
106108
109+ // If exe name is missing, the cli args are invalid
110+ if cur. is_empty ( ) {
111+ return None ;
112+ }
113+
114+ ret_val. push ( OsString :: from ( cur) ) ;
107115 // Skip whitespace.
108- while code_units_iter. next_if_eq ( & & SPACE ) . is_some ( ) { }
109- ret_val. push ( OsString :: from_wide ( & cur) ) ;
116+ while code_units_iter. next_if_eq ( & Ok ( SPACE ) ) . is_some ( ) { }
110117
111118 // Parse the arguments according to these rules:
112119 // * All code units are taken literally except space, quote and caret.
@@ -116,44 +123,36 @@ fn parse_lp_cmd_line(code_units: &[u16]) -> Vec<OsString> {
116123 // * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally.
117124 // * A quote can be escaped if preceded by caret.
118125 // * A caret can be escaped if preceded by caret.
119- let mut cur = Vec :: new ( ) ;
126+ let mut cur = String :: new ( ) ;
120127 let mut in_quotes = false ;
121128 while let Some ( w) = code_units_iter. next ( ) {
122- match * w {
129+ let w = w. ok ( ) ?;
130+ match w {
123131 // break on NULL
124132 NULL => break ,
125133 // If not `in_quotes`, a space or tab ends the argument.
126134 SPACE if !in_quotes => {
127- ret_val. push ( OsString :: from_wide ( & cur[ ..] ) ) ;
135+ ret_val. push ( OsString :: from ( & cur[ ..] ) ) ;
128136 cur. truncate ( 0 ) ;
129137
130138 // Skip whitespace.
131- while code_units_iter. next_if_eq ( & & SPACE ) . is_some ( ) { }
139+ while code_units_iter. next_if_eq ( & Ok ( SPACE ) ) . is_some ( ) { }
132140 }
133141 // Caret can escape quotes or carets
134142 CARET if in_quotes => {
135143 if let Some ( x) = code_units_iter. next ( ) {
136- cur. push ( * x )
144+ cur. push ( x . ok ( ) ? ) ;
137145 }
138146 }
139- // If `in_quotes` and not caret escaped (see above) then a quote either
140- // unsets `in_quote` or is escaped by another quote.
141- QUOTE if in_quotes => match code_units_iter. peek ( ) {
142- // Otherwise set `in_quotes`.
143- Some ( _) => in_quotes = false ,
144- // The end of the command line.
145- // Push `cur` even if empty, which we do by breaking while `in_quotes` is still set.
146- None => break ,
147- } ,
148- // If not `in_quotes` and not BACKSLASH escaped (see above) then a quote sets `in_quote`.
149- QUOTE => in_quotes = true ,
147+ // If quote then flip `in_quotes`
148+ QUOTE => in_quotes = !in_quotes,
150149 // Everything else is always taken literally.
151- _ => cur. push ( * w) ,
150+ _ => cur. push ( w) ,
152151 }
153152 }
154153 // Push the final argument, if any.
155154 if !cur. is_empty ( ) || in_quotes {
156- ret_val. push ( OsString :: from_wide ( & cur[ .. ] ) ) ;
155+ ret_val. push ( OsString :: from ( cur) ) ;
157156 }
158- ret_val
157+ Some ( ret_val)
159158}
0 commit comments