Skip to content

Commit c0934e1

Browse files
committed
Auto merge of #1152 - divergentdave:shim-directory, r=RalfJung
Add directory-related shims This PR adds support for `mkdir`, `rmdir`, `opendir`, `closedir`, and `readdir64_r`. Open directory streams are tracked through a HashMap indexed by pointer locations, which holds directory iterators. Since `DIR` is an opaque type in glibc, I represent them with 1-byte allocations, and then just use their pointers in HashMap lookups. Tests are included to exercise the new functionality.
2 parents 070cecb + 62f9f4c commit c0934e1

File tree

9 files changed

+437
-11
lines changed

9 files changed

+437
-11
lines changed

src/helpers.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -474,15 +474,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
474474
}
475475

476476
/// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what
477-
/// the Unix APIs usually handle. This function returns `Ok(false)` without trying to write if
478-
/// `size` is not large enough to fit the contents of `os_string` plus a null terminator. It
479-
/// returns `Ok(true)` if the writing process was successful.
477+
/// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
478+
/// to write if `size` is not large enough to fit the contents of `os_string` plus a null
479+
/// terminator. It returns `Ok((true, length))` if the writing process was successful. The
480+
/// string length returned does not include the null terminator.
480481
fn write_os_str_to_c_str(
481482
&mut self,
482483
os_str: &OsStr,
483484
scalar: Scalar<Tag>,
484485
size: u64,
485-
) -> InterpResult<'tcx, bool> {
486+
) -> InterpResult<'tcx, (bool, u64)> {
486487
#[cfg(target_os = "unix")]
487488
fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
488489
std::os::unix::ffi::OsStringExt::into_bytes(os_str)
@@ -501,13 +502,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
501502
let bytes = os_str_to_bytes(os_str)?;
502503
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
503504
// terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
504-
if size <= bytes.len() as u64 {
505-
return Ok(false);
505+
let string_length = bytes.len() as u64;
506+
if size <= string_length {
507+
return Ok((false, string_length));
506508
}
507509
self.eval_context_mut()
508510
.memory
509511
.write_bytes(scalar, bytes.iter().copied().chain(iter::once(0u8)))?;
510-
Ok(true)
512+
Ok((true, string_length))
511513
}
512514

513515
fn alloc_os_str_as_c_str(

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub use rustc_mir::interpret::{self, AllocMap, PlaceTy};
3636
pub use crate::shims::dlsym::{Dlsym, EvalContextExt as DlsymEvalContextExt};
3737
pub use crate::shims::env::{EnvVars, EvalContextExt as EnvEvalContextExt};
3838
pub use crate::shims::foreign_items::EvalContextExt as ForeignItemsEvalContextExt;
39-
pub use crate::shims::fs::{EvalContextExt as FileEvalContextExt, FileHandler};
39+
pub use crate::shims::fs::{DirHandler, EvalContextExt as FileEvalContextExt, FileHandler};
4040
pub use crate::shims::intrinsics::EvalContextExt as IntrinsicsEvalContextExt;
4141
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as PanicEvalContextExt};
4242
pub use crate::shims::time::EvalContextExt as TimeEvalContextExt;

src/machine.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ pub struct Evaluator<'tcx> {
120120
pub(crate) validate: bool,
121121

122122
pub(crate) file_handler: FileHandler,
123+
pub(crate) dir_handler: DirHandler,
123124

124125
/// The temporary used for storing the argument of
125126
/// the call to `miri_start_panic` (the panic payload) when unwinding.
@@ -140,6 +141,7 @@ impl<'tcx> Evaluator<'tcx> {
140141
communicate,
141142
validate,
142143
file_handler: Default::default(),
144+
dir_handler: Default::default(),
143145
panic_payload: None,
144146
}
145147
}

src/shims/env.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
124124
// If we cannot get the current directory, we return null
125125
match env::current_dir() {
126126
Ok(cwd) => {
127-
if this.write_os_str_to_c_str(&OsString::from(cwd), buf, size)? {
127+
if this.write_os_str_to_c_str(&OsString::from(cwd), buf, size)?.0 {
128128
return Ok(buf);
129129
}
130130
let erange = this.eval_libc("ERANGE")?;

src/shims/foreign_items/posix.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
109109
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
110110
}
111111

112+
"mkdir" => {
113+
let result = this.mkdir(args[0], args[1])?;
114+
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
115+
}
116+
117+
"rmdir" => {
118+
let result = this.rmdir(args[0])?;
119+
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
120+
}
121+
122+
"closedir" => {
123+
let result = this.closedir(args[0])?;
124+
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
125+
}
126+
112127
"lseek" | "lseek64" => {
113128
let result = this.lseek64(args[0], args[1], args[2])?;
114129
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;

src/shims/foreign_items/posix/linux.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
2727
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
2828
}
2929

30+
// The only reason this is not in the `posix` module is because the `macos` item has a
31+
// different name.
32+
"opendir" => {
33+
let result = this.opendir(args[0])?;
34+
this.write_scalar(result, dest)?;
35+
}
36+
37+
// The `macos` module has a parallel foreign item, `readdir_r`, which uses a different
38+
// struct layout.
39+
"readdir64_r" => {
40+
let result = this.linux_readdir64_r(args[0], args[1], args[2])?;
41+
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
42+
}
43+
3044
// Time related shims
3145

3246
// This is a POSIX function but it has only been tested on linux.

src/shims/foreign_items/posix/macos.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4242
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
4343
}
4444

45+
// The only reason this is not in the `posix` module is because the `linux` item has a
46+
// different name.
47+
"opendir$INODE64" => {
48+
let result = this.opendir(args[0])?;
49+
this.write_scalar(result, dest)?;
50+
}
51+
52+
// The `linux` module has a parallel foreign item, `readdir64_r`, which uses a
53+
// different struct layout.
54+
"readdir_r$INODE64" => {
55+
let result = this.macos_readdir_r(args[0], args[1], args[2])?;
56+
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
57+
}
58+
4559
// Time related shims
4660
"gettimeofday" => {
4761
let result = this.gettimeofday(args[0], args[1])?;

0 commit comments

Comments
 (0)