Skip to content

Commit 742089d

Browse files
committed
Check for correct env keys in Command
Return an error if the key contains an ASCII equals sign (`=`) at the non-first position.
1 parent 62eb605 commit 742089d

File tree

4 files changed

+65
-21
lines changed

4 files changed

+65
-21
lines changed

src/libstd/process.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,27 @@ mod tests {
13741374
}
13751375
}
13761376

1377+
#[test]
1378+
fn test_empty_env_key_is_error() {
1379+
match env_cmd().env("", "value").spawn() {
1380+
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
1381+
Ok(_) => panic!(),
1382+
}
1383+
}
1384+
1385+
#[test]
1386+
fn test_interior_eq_in_env_key_is_error() {
1387+
match env_cmd().env("has-some-==s-inside", "value").spawn() {
1388+
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
1389+
Ok(_) => panic!(),
1390+
}
1391+
}
1392+
1393+
#[test]
1394+
fn test_leading_eq_key_is_no_error() {
1395+
env_cmd().env("=NAME", "value").output().unwrap();
1396+
}
1397+
13771398
/// Test that process creation flags work by debugging a process.
13781399
/// Other creation flags make it hard or impossible to detect
13791400
/// behavioral changes in the process.

src/libstd/sys/unix/process/process_common.rs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub struct Command {
5353
uid: Option<uid_t>,
5454
gid: Option<gid_t>,
5555
saw_nul: bool,
56+
saw_malformed_env_key: bool,
5657
closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>,
5758
stdin: Option<Stdio>,
5859
stdout: Option<Stdio>,
@@ -102,6 +103,7 @@ impl Command {
102103
uid: None,
103104
gid: None,
104105
saw_nul: saw_nul,
106+
saw_malformed_env_key: false,
105107
closures: Vec::new(),
106108
stdin: None,
107109
stdout: None,
@@ -127,7 +129,11 @@ impl Command {
127129
let mut map = HashMap::new();
128130
let mut envp = Vec::new();
129131
for (k, v) in env::vars_os() {
130-
let s = pair_to_key(&k, &v, &mut self.saw_nul);
132+
let mut saw_nul = false;
133+
let mut saw_malformed_env_key = false;
134+
let s = pair_to_key(&k, &v, &mut saw_nul, &mut saw_malformed_env_key);
135+
assert!(!saw_nul);
136+
assert!(!saw_malformed_env_key);
131137
envp.push(s.as_ptr());
132138
map.insert(k, (envp.len() - 1, s));
133139
}
@@ -139,7 +145,8 @@ impl Command {
139145
}
140146

141147
pub fn env(&mut self, key: &OsStr, val: &OsStr) {
142-
let new_key = pair_to_key(key, val, &mut self.saw_nul);
148+
let new_key = pair_to_key(key, val, &mut self.saw_nul, &mut self.saw_malformed_env_key);
149+
143150
let (map, envp) = self.init_env_map();
144151

145152
// If `key` is already present then we just update `envp` in place
@@ -162,6 +169,8 @@ impl Command {
162169
}
163170

164171
pub fn env_remove(&mut self, key: &OsStr) {
172+
pair_to_key(key, OsStr::new(""), &mut self.saw_nul, &mut self.saw_malformed_env_key);
173+
165174
let (map, envp) = self.init_env_map();
166175

167176
// If we actually ended up removing a key, then we need to update the
@@ -193,9 +202,6 @@ impl Command {
193202
self.gid = Some(id);
194203
}
195204

196-
pub fn saw_nul(&self) -> bool {
197-
self.saw_nul
198-
}
199205
pub fn get_envp(&self) -> &Option<Vec<*const c_char>> {
200206
&self.envp
201207
}
@@ -237,6 +243,18 @@ impl Command {
237243
self.stderr = Some(stderr);
238244
}
239245

246+
pub fn check_malformed(&self) -> io::Result<()> {
247+
if self.saw_nul {
248+
return Err(io::Error::new(io::ErrorKind::InvalidInput,
249+
"nul byte found in provided data"));
250+
}
251+
if self.saw_malformed_env_key {
252+
return Err(io::Error::new(io::ErrorKind::InvalidInput,
253+
"malformed env key in provided data"));
254+
}
255+
Ok(())
256+
}
257+
240258
pub fn setup_io(&self, default: Stdio, needs_stdin: bool)
241259
-> io::Result<(StdioPipes, ChildPipes)> {
242260
let null = Stdio::Null;
@@ -261,6 +279,13 @@ impl Command {
261279
}
262280
}
263281

282+
fn check_env_key(s: &OsStr, saw_malformed: &mut bool) {
283+
let bytes = s.as_bytes();
284+
if bytes.is_empty() || bytes[1..].contains(&b'=') {
285+
*saw_malformed = true;
286+
}
287+
}
288+
264289
fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString {
265290
CString::new(s.as_bytes()).unwrap_or_else(|_e| {
266291
*saw_nul = true;
@@ -325,15 +350,19 @@ impl ChildStdio {
325350
}
326351
}
327352

328-
fn pair_to_key(key: &OsStr, value: &OsStr, saw_nul: &mut bool) -> CString {
353+
fn pair_to_key(key: &OsStr, value: &OsStr, saw_nul: &mut bool, saw_malformed: &mut bool)
354+
-> CString
355+
{
356+
check_env_key(key, saw_malformed);
357+
329358
let (key, value) = (key.as_bytes(), value.as_bytes());
330359
let mut v = Vec::with_capacity(key.len() + value.len() + 1);
331360
v.extend(key);
332361
v.push(b'=');
333362
v.extend(value);
334363
CString::new(v).unwrap_or_else(|_e| {
335364
*saw_nul = true;
336-
CString::new("foo=bar").unwrap()
365+
CString::new("<ENV_WITH_NUL>").unwrap()
337366
})
338367
}
339368

src/libstd/sys/unix/process/process_fuchsia.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ use sys::process::process_common::*;
2323
impl Command {
2424
pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
2525
-> io::Result<(Process, StdioPipes)> {
26-
if self.saw_nul() {
27-
return Err(io::Error::new(io::ErrorKind::InvalidInput,
28-
"nul byte found in provided data"));
29-
}
26+
self.check_malformed()?;
3027

3128
let (ours, theirs) = self.setup_io(default, needs_stdin)?;
3229

@@ -36,9 +33,9 @@ impl Command {
3633
}
3734

3835
pub fn exec(&mut self, default: Stdio) -> io::Error {
39-
if self.saw_nul() {
40-
return io::Error::new(io::ErrorKind::InvalidInput,
41-
"nul byte found in provided data")
36+
match self.check_malformed() {
37+
Ok(()) => {},
38+
Err(e) => return e,
4239
}
4340

4441
match self.setup_io(default, true) {

src/libstd/sys/unix/process/process_unix.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,7 @@ impl Command {
2727

2828
const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
2929

30-
if self.saw_nul() {
31-
return Err(io::Error::new(ErrorKind::InvalidInput,
32-
"nul byte found in provided data"));
33-
}
30+
self.check_malformed()?;
3431

3532
let (ours, theirs) = self.setup_io(default, needs_stdin)?;
3633
let (input, output) = sys::pipe::anon_pipe()?;
@@ -100,9 +97,9 @@ impl Command {
10097
}
10198

10299
pub fn exec(&mut self, default: Stdio) -> io::Error {
103-
if self.saw_nul() {
104-
return io::Error::new(ErrorKind::InvalidInput,
105-
"nul byte found in provided data")
100+
match self.check_malformed() {
101+
Ok(()) => {},
102+
Err(e) => return e,
106103
}
107104

108105
match self.setup_io(default, true) {

0 commit comments

Comments
 (0)