@@ -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
@@ -16,31 +15,27 @@ pub struct Args {
1615pub fn args ( ) -> Args {
1716 // SAFETY: Each loaded image has an image handle that supports EFI_LOADED_IMAGE_PROTOCOL
1817 let protocol =
19- helpers:: current_handle_protocol :: < loaded_image:: Protocol > ( loaded_image:: PROTOCOL_GUID )
18+ helpers:: image_handle_protocol :: < loaded_image:: Protocol > ( loaded_image:: PROTOCOL_GUID )
2019 . unwrap ( ) ;
2120
2221 let lp_size = unsafe { ( * protocol. as_ptr ( ) ) . load_options_size } as usize ;
23- let lp_size: usize = lp_size / size_of :: < u16 > ( ) ;
2422
25- if lp_size <= 0 {
23+ // Break if we are sure that it cannot be UTF-16
24+ if lp_size < size_of :: < u16 > ( ) || lp_size % size_of :: < u16 > ( ) != 0 {
2625 return Args {
2726 parsed_args_list : Vec :: from ( [ current_exe ( ) . map ( Into :: into) . unwrap_or_default ( ) ] )
2827 . into_iter ( ) ,
2928 } ;
3029 }
3130
3231 let lp_cmd_line = unsafe {
32+ let lp_size = lp_size / size_of :: < u16 > ( ) ;
3333 let temp = ( * protocol. as_ptr ( ) ) . load_options as * const u16 ;
3434 crate :: slice:: from_raw_parts ( temp, lp_size)
3535 } ;
3636
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- } ;
43-
37+ let vec_args = parse_lp_cmd_line ( lp_cmd_line)
38+ . unwrap_or ( Vec :: from ( [ current_exe ( ) . map ( Into :: into) . unwrap_or_default ( ) ] ) ) ;
4439 Args { parsed_args_list : vec_args. into_iter ( ) }
4540}
4641
@@ -78,20 +73,25 @@ impl DoubleEndedIterator for Args {
7873///
7974/// This implementation is based on what is defined in Section 3.4 of
8075/// [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 ;
76+ ///
77+ /// Return None in the following cases:
78+ /// - Invalid UTF-16 (unpaired surrogate)
79+ /// - Empty/improper arguments
80+ fn parse_lp_cmd_line ( code_units : & [ u16 ] ) -> Option < Vec < OsString > > {
81+ const QUOTE : char = '"' ;
82+ const SPACE : char = ' ' ;
83+ const CARET : char = '^' ;
84+ const NULL : char = '\0' ;
8685
8786 let mut ret_val = Vec :: new ( ) ;
88- let mut code_units_iter = code_units. iter ( ) . peekable ( ) ;
87+ let mut code_units_iter = char :: decode_utf16 ( code_units. iter ( ) . cloned ( ) ) . peekable ( ) ;
8988
9089 // The executable name at the beginning is special.
9190 let mut in_quotes = false ;
92- let mut cur = Vec :: new ( ) ;
91+ let mut cur = String :: new ( ) ;
9392 while let Some ( w) = code_units_iter. next ( ) {
94- match * w {
93+ let w = w. ok ( ) ?;
94+ match w {
9595 // break on NULL
9696 NULL => break ,
9797 // A quote mark always toggles `in_quotes` no matter what because
@@ -100,13 +100,13 @@ fn parse_lp_cmd_line(code_units: &[u16]) -> Vec<OsString> {
100100 // If not `in_quotes` then whitespace ends argv[0].
101101 SPACE if !in_quotes => break ,
102102 // In all other cases the code unit is taken literally.
103- _ => cur. push ( * w) ,
103+ _ => cur. push ( w) ,
104104 }
105105 }
106106
107107 // Skip whitespace.
108- while code_units_iter. next_if_eq ( & & SPACE ) . is_some ( ) { }
109- ret_val. push ( OsString :: from_wide ( & cur) ) ;
108+ while code_units_iter. next_if_eq ( & Ok ( SPACE ) ) . is_some ( ) { }
109+ ret_val. push ( OsString :: from ( cur) ) ;
110110
111111 // Parse the arguments according to these rules:
112112 // * All code units are taken literally except space, quote and caret.
@@ -116,44 +116,37 @@ fn parse_lp_cmd_line(code_units: &[u16]) -> Vec<OsString> {
116116 // * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally.
117117 // * A quote can be escaped if preceded by caret.
118118 // * A caret can be escaped if preceded by caret.
119- let mut cur = Vec :: new ( ) ;
119+ let mut cur = String :: new ( ) ;
120120 let mut in_quotes = false ;
121121 while let Some ( w) = code_units_iter. next ( ) {
122- match * w {
122+ let w = w. ok ( ) ?;
123+ match w {
123124 // break on NULL
124125 NULL => break ,
125126 // If not `in_quotes`, a space or tab ends the argument.
126127 SPACE if !in_quotes => {
127- ret_val. push ( OsString :: from_wide ( & cur[ ..] ) ) ;
128+ ret_val. push ( OsString :: from ( & cur[ ..] ) ) ;
128129 cur. truncate ( 0 ) ;
129130
130131 // Skip whitespace.
131- while code_units_iter. next_if_eq ( & & SPACE ) . is_some ( ) { }
132+ while code_units_iter. next_if_eq ( & Ok ( SPACE ) ) . is_some ( ) { }
132133 }
133134 // Caret can escape quotes or carets
134135 CARET if in_quotes => {
135136 if let Some ( x) = code_units_iter. next ( ) {
136- cur. push ( * x )
137+ cur. push ( x . ok ( ) ? ) ;
137138 }
138139 }
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 ,
140+ // If quote then flip `in_quotes`
141+ QUOTE => in_quotes = !in_quotes,
150142 // Everything else is always taken literally.
151- _ => cur. push ( * w) ,
143+ _ => cur. push ( w) ,
152144 }
153145 }
154146 // Push the final argument, if any.
155147 if !cur. is_empty ( ) || in_quotes {
156- ret_val. push ( OsString :: from_wide ( & cur[ .. ] ) ) ;
148+ ret_val. push ( OsString :: from ( cur) ) ;
157149 }
158- ret_val
150+
151+ if ret_val. is_empty ( ) { None } else { Some ( ret_val) }
159152}
0 commit comments