Skip to content

Commit a05f2aa

Browse files
committed
Auto merge of #993 - christianpoveda:os_string_helper, r=RalfJung
Add OsString from/to bytes helper functions Related issue: #989 r? @RalfJung
2 parents 7e8b17c + de12cbc commit a05f2aa

File tree

4 files changed

+63
-42
lines changed

4 files changed

+63
-42
lines changed

src/helpers.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::mem;
2+
use std::ffi::{OsStr, OsString};
23

34
use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
45
use rustc::mir;
@@ -402,4 +403,60 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
402403
}
403404
}
404405
}
406+
407+
/// Helper function to read an OsString from a null-terminated sequence of bytes, which is what
408+
/// the Unix APIs usually handle.
409+
fn read_os_string_from_c_string(&mut self, scalar: Scalar<Tag>) -> InterpResult<'tcx, OsString> {
410+
let bytes = self.eval_context_mut().memory.read_c_str(scalar)?;
411+
Ok(bytes_to_os_str(bytes)?.into())
412+
}
413+
414+
/// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what
415+
/// the Unix APIs usually handle. This function returns `Ok(false)` without trying to write if
416+
/// `size` is not large enough to fit the contents of `os_string` plus a null terminator. It
417+
/// returns `Ok(true)` if the writing process was successful.
418+
fn write_os_str_to_c_string(
419+
&mut self,
420+
os_str: &OsStr,
421+
scalar: Scalar<Tag>,
422+
size: u64
423+
) -> InterpResult<'tcx, bool> {
424+
let bytes = os_str_to_bytes(os_str)?;
425+
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
426+
// terminator to memory using the `ptr` pointer would cause an overflow.
427+
if size <= bytes.len() as u64 {
428+
return Ok(false);
429+
}
430+
// FIXME: We should use `Iterator::chain` instead when rust-lang/rust#65704 lands.
431+
self.eval_context_mut().memory.write_bytes(scalar, [bytes, &[0]].concat())?;
432+
Ok(true)
433+
}
434+
}
435+
436+
#[cfg(target_os = "unix")]
437+
fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
438+
std::os::unix::ffi::OsStringExt::into_bytes(os_str)
439+
}
440+
441+
#[cfg(target_os = "unix")]
442+
fn bytes_to_os_str<'tcx, 'a>(bytes: &'a[u8]) -> InterpResult<'tcx, &'a OsStr> {
443+
Ok(std::os::unix::ffi::OsStringExt::from_bytes(bytes))
444+
}
445+
446+
// On non-unix platforms the best we can do to transform bytes from/to OS strings is to do the
447+
// intermediate transformation into strings. Which invalidates non-utf8 paths that are actually
448+
// valid.
449+
#[cfg(not(target_os = "unix"))]
450+
fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
451+
os_str
452+
.to_str()
453+
.map(|s| s.as_bytes())
454+
.ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into())
455+
}
456+
457+
#[cfg(not(target_os = "unix"))]
458+
fn bytes_to_os_str<'tcx, 'a>(bytes: &'a[u8]) -> InterpResult<'tcx, &'a OsStr> {
459+
let s = std::str::from_utf8(bytes)
460+
.map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", bytes))?;
461+
Ok(&OsStr::new(s))
405462
}

src/shims/env.rs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use std::collections::HashMap;
2+
use std::ffi::OsString;
23
use std::env;
3-
use std::path::Path;
44

55
use crate::stacked_borrows::Tag;
66
use crate::*;
7+
78
use rustc::ty::layout::Size;
89
use rustc_mir::interpret::{Memory, Pointer};
910

@@ -127,18 +128,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
127128
// If we cannot get the current directory, we return null
128129
match env::current_dir() {
129130
Ok(cwd) => {
130-
// It is not clear what happens with non-utf8 paths here
131-
let mut bytes = cwd.display().to_string().into_bytes();
132-
// If `size` is smaller or equal than the `bytes.len()`, writing `bytes` plus the
133-
// required null terminator to memory using the `buf` pointer would cause an
134-
// overflow. The desired behavior in this case is to return null.
135-
if (bytes.len() as u64) < size {
136-
// We add a `/0` terminator
137-
bytes.push(0);
138-
// This is ok because the buffer was strictly larger than `bytes`, so after
139-
// adding the null terminator, the buffer size is larger or equal to
140-
// `bytes.len()`, meaning that `bytes` actually fit inside tbe buffer.
141-
this.memory.write_bytes(buf, bytes.iter().copied())?;
131+
if this.write_os_str_to_c_string(&OsString::from(cwd), buf, size)? {
142132
return Ok(buf);
143133
}
144134
let erange = this.eval_libc("ERANGE")?;
@@ -154,14 +144,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
154144

155145
this.check_no_isolation("chdir")?;
156146

157-
let path_bytes = this
158-
.memory
159-
.read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
160-
161-
let path = Path::new(
162-
std::str::from_utf8(path_bytes)
163-
.map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?,
164-
);
147+
let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;
165148

166149
match env::set_current_dir(path) {
167150
Ok(()) => Ok(0),

src/shims/fs.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
9595
throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
9696
}
9797

98-
let path_bytes = this
99-
.memory
100-
.read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
101-
let path = std::str::from_utf8(path_bytes)
102-
.map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?;
98+
let path: std::path::PathBuf = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?.into();
10399

104100
let fd = options.open(path).map(|file| {
105101
let mut fh = &mut this.machine.file_handler;
@@ -214,11 +210,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
214210

215211
this.check_no_isolation("unlink")?;
216212

217-
let path_bytes = this
218-
.memory
219-
.read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
220-
let path = std::str::from_utf8(path_bytes)
221-
.map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?;
213+
let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;
222214

223215
let result = remove_file(path).map(|_| 0);
224216

tests/compile-fail/chdir_invalid_path.rs

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)