@@ -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
@@ -16,31 +15,27 @@ pub struct Args {
16
15
pub fn args ( ) -> Args {
17
16
// SAFETY: Each loaded image has an image handle that supports EFI_LOADED_IMAGE_PROTOCOL
18
17
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 )
20
19
. unwrap ( ) ;
21
20
22
21
let lp_size = unsafe { ( * protocol. as_ptr ( ) ) . load_options_size } as usize ;
23
- let lp_size: usize = lp_size / size_of :: < u16 > ( ) ;
24
22
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 {
26
25
return Args {
27
26
parsed_args_list : Vec :: from ( [ current_exe ( ) . map ( Into :: into) . unwrap_or_default ( ) ] )
28
27
. into_iter ( ) ,
29
28
} ;
30
29
}
31
30
32
31
let lp_cmd_line = unsafe {
32
+ let lp_size = lp_size / size_of :: < u16 > ( ) ;
33
33
let temp = ( * protocol. as_ptr ( ) ) . load_options as * const u16 ;
34
34
crate :: slice:: from_raw_parts ( temp, lp_size)
35
35
} ;
36
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
- } ;
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 ( ) ] ) ) ;
44
39
Args { parsed_args_list : vec_args. into_iter ( ) }
45
40
}
46
41
@@ -78,20 +73,25 @@ impl DoubleEndedIterator for Args {
78
73
///
79
74
/// This implementation is based on what is defined in Section 3.4 of
80
75
/// [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' ;
86
85
87
86
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 ( ) ;
89
88
90
89
// The executable name at the beginning is special.
91
90
let mut in_quotes = false ;
92
- let mut cur = Vec :: new ( ) ;
91
+ let mut cur = String :: new ( ) ;
93
92
while let Some ( w) = code_units_iter. next ( ) {
94
- match * w {
93
+ let w = w. ok ( ) ?;
94
+ match w {
95
95
// break on NULL
96
96
NULL => break ,
97
97
// 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> {
100
100
// If not `in_quotes` then whitespace ends argv[0].
101
101
SPACE if !in_quotes => break ,
102
102
// In all other cases the code unit is taken literally.
103
- _ => cur. push ( * w) ,
103
+ _ => cur. push ( w) ,
104
104
}
105
105
}
106
106
107
107
// 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) ) ;
110
110
111
111
// Parse the arguments according to these rules:
112
112
// * 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> {
116
116
// * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally.
117
117
// * A quote can be escaped if preceded by caret.
118
118
// * A caret can be escaped if preceded by caret.
119
- let mut cur = Vec :: new ( ) ;
119
+ let mut cur = String :: new ( ) ;
120
120
let mut in_quotes = false ;
121
121
while let Some ( w) = code_units_iter. next ( ) {
122
- match * w {
122
+ let w = w. ok ( ) ?;
123
+ match w {
123
124
// break on NULL
124
125
NULL => break ,
125
126
// If not `in_quotes`, a space or tab ends the argument.
126
127
SPACE if !in_quotes => {
127
- ret_val. push ( OsString :: from_wide ( & cur[ ..] ) ) ;
128
+ ret_val. push ( OsString :: from ( & cur[ ..] ) ) ;
128
129
cur. truncate ( 0 ) ;
129
130
130
131
// 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 ( ) { }
132
133
}
133
134
// Caret can escape quotes or carets
134
135
CARET if in_quotes => {
135
136
if let Some ( x) = code_units_iter. next ( ) {
136
- cur. push ( * x )
137
+ cur. push ( x . ok ( ) ? ) ;
137
138
}
138
139
}
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,
150
142
// Everything else is always taken literally.
151
- _ => cur. push ( * w) ,
143
+ _ => cur. push ( w) ,
152
144
}
153
145
}
154
146
// Push the final argument, if any.
155
147
if !cur. is_empty ( ) || in_quotes {
156
- ret_val. push ( OsString :: from_wide ( & cur[ .. ] ) ) ;
148
+ ret_val. push ( OsString :: from ( cur) ) ;
157
149
}
158
- ret_val
150
+
151
+ if ret_val. is_empty ( ) { None } else { Some ( ret_val) }
159
152
}
0 commit comments