Skip to content

Commit 5b0dea6

Browse files
committed
Fixes from PR
- rename to image_handle_protocol - verify that cli args are valid UTF-16 - fix comments - lazy exe - check load_options alignment Signed-off-by: Ayush Singh <[email protected]>
1 parent 9f26c8b commit 5b0dea6

File tree

2 files changed

+52
-53
lines changed

2 files changed

+52
-53
lines changed

library/std/src/sys/uefi/args.rs

Lines changed: 51 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::ffi::OsString;
55
use crate::fmt;
66
use crate::iter::Iterator;
77
use crate::mem::size_of;
8-
use crate::os::uefi::ffi::OsStringExt;
98
use crate::sys::uefi::helpers;
109
use crate::vec;
1110

@@ -14,34 +13,32 @@ pub struct Args {
1413
}
1514

1615
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.
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

4744
impl 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
}

library/std/src/sys/uefi/helpers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ pub(crate) unsafe fn close_event(evt: NonNull<crate::ffi::c_void>) -> io::Result
142142

143143
/// Get the Protocol for current system handle.
144144
/// Note: Some protocols need to be manually freed. It is the callers responsibility to do so.
145-
pub(crate) fn current_handle_protocol<T>(protocol_guid: Guid) -> Option<NonNull<T>> {
145+
pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> Option<NonNull<T>> {
146146
let system_handle = uefi::env::try_image_handle()?;
147147
open_protocol(system_handle, protocol_guid).ok()
148148
}

0 commit comments

Comments
 (0)