Skip to content

Commit 9f26c8b

Browse files
committed
Use load_options_size along with NULL termination
- Since the spec does not state NULL termination, use load_options_size to prevent buffer overflow Signed-off-by: Ayush Singh <ayushdevel1325@gmail.com>
1 parent 87587f0 commit 9f26c8b

File tree

1 file changed

+44
-33
lines changed

1 file changed

+44
-33
lines changed

Diff for: library/std/src/sys/uefi/args.rs

+44-33
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ use r_efi::protocols::loaded_image;
33
use crate::env::current_exe;
44
use crate::ffi::OsString;
55
use crate::fmt;
6-
use crate::num::NonZeroU16;
6+
use crate::iter::Iterator;
7+
use crate::mem::size_of;
78
use crate::os::uefi::ffi::OsStringExt;
89
use crate::sys::uefi::helpers;
9-
use crate::sys_common::wstr::WStrUnits;
1010
use crate::vec;
1111

1212
pub struct Args {
@@ -19,10 +19,26 @@ pub fn args() -> Args {
1919
helpers::current_handle_protocol::<loaded_image::Protocol>(loaded_image::PROTOCOL_GUID)
2020
.unwrap();
2121

22-
let lp_cmd_line = unsafe { (*protocol.as_ptr()).load_options as *const u16 };
23-
let vec_args = match unsafe { WStrUnits::new(lp_cmd_line) } {
24-
Some(code_units) => parse_lp_cmd_line(code_units),
25-
None => Vec::from([current_exe().map(Into::into).unwrap_or_default()]),
22+
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+
};
30+
}
31+
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
2642
};
2743

2844
Args { parsed_args_list: vec_args.into_iter() }
@@ -62,30 +78,34 @@ impl DoubleEndedIterator for Args {
6278
///
6379
/// This implementation is based on what is defined in Section 3.4 of
6480
/// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_Spec_2_0.pdf)
65-
fn parse_lp_cmd_line<'a>(mut code_units: WStrUnits<'a>) -> Vec<OsString> {
66-
const QUOTE: NonZeroU16 = non_zero_u16(b'"' as u16);
67-
const SPACE: NonZeroU16 = non_zero_u16(b' ' as u16);
68-
const CARET: NonZeroU16 = non_zero_u16(b'^' as u16);
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;
6986

7087
let mut ret_val = Vec::new();
88+
let mut code_units_iter = code_units.iter().peekable();
7189

7290
// The executable name at the beginning is special.
7391
let mut in_quotes = false;
7492
let mut cur = Vec::new();
75-
for w in &mut code_units {
76-
match w {
93+
while let Some(w) = code_units_iter.next() {
94+
match *w {
95+
// break on NULL
96+
NULL => break,
7797
// A quote mark always toggles `in_quotes` no matter what because
7898
// there are no escape characters when parsing the executable name.
7999
QUOTE => in_quotes = !in_quotes,
80100
// If not `in_quotes` then whitespace ends argv[0].
81101
SPACE if !in_quotes => break,
82102
// In all other cases the code unit is taken literally.
83-
_ => cur.push(w.get()),
103+
_ => cur.push(*w),
84104
}
85105
}
86106

87107
// Skip whitespace.
88-
code_units.advance_while(|w| w == SPACE);
108+
while code_units_iter.next_if_eq(&&SPACE).is_some() {}
89109
ret_val.push(OsString::from_wide(&cur));
90110

91111
// Parse the arguments according to these rules:
@@ -98,25 +118,27 @@ fn parse_lp_cmd_line<'a>(mut code_units: WStrUnits<'a>) -> Vec<OsString> {
98118
// * A caret can be escaped if preceded by caret.
99119
let mut cur = Vec::new();
100120
let mut in_quotes = false;
101-
while let Some(w) = code_units.next() {
102-
match w {
121+
while let Some(w) = code_units_iter.next() {
122+
match *w {
123+
// break on NULL
124+
NULL => break,
103125
// If not `in_quotes`, a space or tab ends the argument.
104126
SPACE if !in_quotes => {
105127
ret_val.push(OsString::from_wide(&cur[..]));
106128
cur.truncate(0);
107129

108130
// Skip whitespace.
109-
code_units.advance_while(|w| w == SPACE);
131+
while code_units_iter.next_if_eq(&&SPACE).is_some() {}
110132
}
111133
// Caret can escape quotes or carets
112134
CARET if in_quotes => {
113-
if let Some(x) = code_units.next() {
114-
cur.push(x.get())
135+
if let Some(x) = code_units_iter.next() {
136+
cur.push(*x)
115137
}
116138
}
117-
// If `in_quotes` and not backslash escaped (see above) then a quote either
139+
// If `in_quotes` and not caret escaped (see above) then a quote either
118140
// unsets `in_quote` or is escaped by another quote.
119-
QUOTE if in_quotes => match code_units.peek() {
141+
QUOTE if in_quotes => match code_units_iter.peek() {
120142
// Otherwise set `in_quotes`.
121143
Some(_) => in_quotes = false,
122144
// The end of the command line.
@@ -126,7 +148,7 @@ fn parse_lp_cmd_line<'a>(mut code_units: WStrUnits<'a>) -> Vec<OsString> {
126148
// If not `in_quotes` and not BACKSLASH escaped (see above) then a quote sets `in_quote`.
127149
QUOTE => in_quotes = true,
128150
// Everything else is always taken literally.
129-
_ => cur.push(w.get()),
151+
_ => cur.push(*w),
130152
}
131153
}
132154
// Push the final argument, if any.
@@ -135,14 +157,3 @@ fn parse_lp_cmd_line<'a>(mut code_units: WStrUnits<'a>) -> Vec<OsString> {
135157
}
136158
ret_val
137159
}
138-
139-
/// This is the const equivalent to `NonZeroU16::new(n).unwrap()`
140-
///
141-
/// FIXME: This can be removed once `Option::unwrap` is stably const.
142-
/// See the `const_option` feature (#67441).
143-
const fn non_zero_u16(n: u16) -> NonZeroU16 {
144-
match NonZeroU16::new(n) {
145-
Some(n) => n,
146-
None => panic!("called `unwrap` on a `None` value"),
147-
}
148-
}

0 commit comments

Comments
 (0)