@@ -5,7 +5,6 @@ use crate::ffi::OsString;
5
5
use crate :: fmt;
6
6
use crate :: iter:: Iterator ;
7
7
use crate :: mem:: size_of;
8
- use crate :: os:: uefi:: ffi:: OsStringExt ;
9
8
use crate :: sys:: uefi:: helpers;
10
9
use crate :: vec;
11
10
@@ -14,34 +13,32 @@ pub struct Args {
14
13
}
15
14
16
15
pub 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.
18
20
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 )
20
22
. unwrap ( ) ;
21
23
22
24
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 ( ) } ;
30
28
}
29
+ let lp_size = lp_size / size_of :: < u16 > ( ) ;
31
30
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) } ;
43
36
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
+ }
45
42
}
46
43
47
44
impl fmt:: Debug for Args {
@@ -78,20 +75,25 @@ impl DoubleEndedIterator for Args {
78
75
///
79
76
/// This implementation is based on what is defined in Section 3.4 of
80
77
/// [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' ;
86
87
87
88
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 ( ) ;
89
90
90
91
// The executable name at the beginning is special.
91
92
let mut in_quotes = false ;
92
- let mut cur = Vec :: new ( ) ;
93
+ let mut cur = String :: new ( ) ;
93
94
while let Some ( w) = code_units_iter. next ( ) {
94
- match * w {
95
+ let w = w. ok ( ) ?;
96
+ match w {
95
97
// break on NULL
96
98
NULL => break ,
97
99
// 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> {
100
102
// If not `in_quotes` then whitespace ends argv[0].
101
103
SPACE if !in_quotes => break ,
102
104
// In all other cases the code unit is taken literally.
103
- _ => cur. push ( * w) ,
105
+ _ => cur. push ( w) ,
104
106
}
105
107
}
106
108
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) ) ;
107
115
// 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 ( ) { }
110
117
111
118
// Parse the arguments according to these rules:
112
119
// * 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> {
116
123
// * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally.
117
124
// * A quote can be escaped if preceded by caret.
118
125
// * A caret can be escaped if preceded by caret.
119
- let mut cur = Vec :: new ( ) ;
126
+ let mut cur = String :: new ( ) ;
120
127
let mut in_quotes = false ;
121
128
while let Some ( w) = code_units_iter. next ( ) {
122
- match * w {
129
+ let w = w. ok ( ) ?;
130
+ match w {
123
131
// break on NULL
124
132
NULL => break ,
125
133
// If not `in_quotes`, a space or tab ends the argument.
126
134
SPACE if !in_quotes => {
127
- ret_val. push ( OsString :: from_wide ( & cur[ ..] ) ) ;
135
+ ret_val. push ( OsString :: from ( & cur[ ..] ) ) ;
128
136
cur. truncate ( 0 ) ;
129
137
130
138
// 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 ( ) { }
132
140
}
133
141
// Caret can escape quotes or carets
134
142
CARET if in_quotes => {
135
143
if let Some ( x) = code_units_iter. next ( ) {
136
- cur. push ( * x )
144
+ cur. push ( x . ok ( ) ? ) ;
137
145
}
138
146
}
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,
150
149
// Everything else is always taken literally.
151
- _ => cur. push ( * w) ,
150
+ _ => cur. push ( w) ,
152
151
}
153
152
}
154
153
// Push the final argument, if any.
155
154
if !cur. is_empty ( ) || in_quotes {
156
- ret_val. push ( OsString :: from_wide ( & cur[ .. ] ) ) ;
155
+ ret_val. push ( OsString :: from ( cur) ) ;
157
156
}
158
- ret_val
157
+ Some ( ret_val)
159
158
}
0 commit comments