Skip to content

Commit 79b1f91

Browse files
committed
First version of file handling
1 parent 402129c commit 79b1f91

File tree

7 files changed

+217
-0
lines changed

7 files changed

+217
-0
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub use crate::shims::intrinsics::EvalContextExt as IntrinsicsEvalContextExt;
3434
pub use crate::shims::tls::{EvalContextExt as TlsEvalContextExt, TlsData};
3535
pub use crate::shims::dlsym::{Dlsym, EvalContextExt as DlsymEvalContextExt};
3636
pub use crate::shims::env::{EnvVars, EvalContextExt as EnvEvalContextExt};
37+
pub use crate::shims::io::{FileHandler, EvalContextExt as FileEvalContextExt};
3738
pub use crate::operator::EvalContextExt as OperatorEvalContextExt;
3839
pub use crate::range_map::RangeMap;
3940
pub use crate::helpers::{EvalContextExt as HelpersEvalContextExt};

src/machine.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ pub struct Evaluator<'tcx> {
9696
/// If enabled, the `env_vars` field is populated with the host env vars during initialization
9797
/// and random number generation is delegated to the host.
9898
pub(crate) communicate: bool,
99+
100+
pub(crate) file_handler: FileHandler,
99101
}
100102

101103
impl<'tcx> Evaluator<'tcx> {
@@ -110,6 +112,7 @@ impl<'tcx> Evaluator<'tcx> {
110112
last_error: 0,
111113
tls: TlsData::default(),
112114
communicate,
115+
file_handler: Default::default(),
113116
}
114117
}
115118
}

src/shims/foreign_items.rs

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

449+
"open" | "open64" => {
450+
let result = this.open(args[0], args[1])?;
451+
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
452+
}
453+
454+
"fcntl" => {
455+
let result = this.fcntl(args[0], args[1], args.get(2).cloned())?;
456+
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
457+
}
458+
459+
"close" => {
460+
let result = this.close(args[0])?;
461+
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
462+
}
463+
464+
"read" => {
465+
let result = this.read(args[0], args[1], args[2])?;
466+
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
467+
}
468+
449469
"write" => {
450470
let fd = this.read_scalar(args[0])?.to_i32()?;
451471
let buf = this.read_scalar(args[1])?.not_undef()?;

src/shims/io.rs

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
use std::collections::HashMap;
2+
use std::fs::File;
3+
use std::io::Read;
4+
5+
use crate::stacked_borrows::Tag;
6+
use crate::*;
7+
8+
#[derive(Default)]
9+
pub struct FileHandler {
10+
files: HashMap<i32, File>,
11+
flags: HashMap<i32, i32>,
12+
low: i32,
13+
}
14+
15+
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
16+
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
17+
fn open(
18+
&mut self,
19+
path_op: OpTy<'tcx, Tag>,
20+
flag_op: OpTy<'tcx, Tag>,
21+
) -> InterpResult<'tcx, i32> {
22+
let this = self.eval_context_mut();
23+
24+
if !this.machine.communicate {
25+
throw_unsup_format!("`open` not available when isolation is enabled")
26+
}
27+
28+
let flag = this.read_scalar(flag_op)?.to_i32()?;
29+
30+
if flag
31+
!= this
32+
.eval_path_scalar(&["libc", "O_RDONLY"])?
33+
.unwrap()
34+
.to_i32()?
35+
&& flag
36+
!= this
37+
.eval_path_scalar(&["libc", "O_CLOEXEC"])?
38+
.unwrap()
39+
.to_i32()?
40+
{
41+
throw_unsup_format!("Unsupported flag {:#x}", flag);
42+
}
43+
44+
let path_bytes = this
45+
.memory()
46+
.read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
47+
let path = std::str::from_utf8(path_bytes)
48+
.map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?;
49+
50+
match File::open(path) {
51+
Ok(file) => {
52+
let mut fh = &mut this.machine.file_handler;
53+
fh.low += 1;
54+
fh.files.insert(fh.low, file);
55+
fh.flags.insert(fh.low, flag);
56+
Ok(fh.low)
57+
}
58+
59+
Err(e) => {
60+
this.machine.last_error = e.raw_os_error().unwrap() as u32;
61+
Ok(-1)
62+
}
63+
}
64+
}
65+
66+
fn fcntl(
67+
&mut self,
68+
fd_op: OpTy<'tcx, Tag>,
69+
cmd_op: OpTy<'tcx, Tag>,
70+
arg_op: Option<OpTy<'tcx, Tag>>,
71+
) -> InterpResult<'tcx, i32> {
72+
let this = self.eval_context_mut();
73+
74+
if !this.machine.communicate {
75+
throw_unsup_format!("`open` not available when isolation is enabled")
76+
}
77+
78+
let fd = this.read_scalar(fd_op)?.to_i32()?;
79+
let cmd = this.read_scalar(cmd_op)?.to_i32()?;
80+
81+
if cmd
82+
== this
83+
.eval_path_scalar(&["libc", "F_SETFD"])?
84+
.unwrap()
85+
.to_i32()?
86+
{
87+
let flag = this.read_scalar(arg_op.unwrap())?.to_i32()?;
88+
this.machine.file_handler.flags.insert(fd, flag);
89+
Ok(0)
90+
} else if cmd
91+
== this
92+
.eval_path_scalar(&["libc", "F_GETFD"])?
93+
.unwrap()
94+
.to_i32()?
95+
{
96+
if let Some(flag) = this.machine.file_handler.flags.get(&fd) {
97+
Ok(*flag)
98+
} else {
99+
this.machine.last_error = this
100+
.eval_path_scalar(&["libc", "EBADF"])?
101+
.unwrap()
102+
.to_u32()?;
103+
Ok(-1)
104+
}
105+
} else {
106+
throw_unsup_format!("Unsupported command {:#x}", cmd);
107+
}
108+
}
109+
110+
fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
111+
let this = self.eval_context_mut();
112+
113+
if !this.machine.communicate {
114+
throw_unsup_format!("`open` not available when isolation is enabled")
115+
}
116+
117+
let fd = this.read_scalar(fd_op)?.to_i32()?;
118+
119+
if let Some(file) = this.machine.file_handler.files.remove(&fd) {
120+
match file.sync_all() {
121+
Ok(()) => Ok(0),
122+
Err(e) => {
123+
this.machine.last_error = e.raw_os_error().unwrap() as u32;
124+
Ok(-1)
125+
}
126+
}
127+
} else {
128+
this.machine.last_error = this
129+
.eval_path_scalar(&["libc", "EBADF"])?
130+
.unwrap()
131+
.to_u32()?;
132+
Ok(-1)
133+
}
134+
}
135+
136+
fn read(
137+
&mut self,
138+
fd_op: OpTy<'tcx, Tag>,
139+
buf_op: OpTy<'tcx, Tag>,
140+
count_op: OpTy<'tcx, Tag>,
141+
) -> InterpResult<'tcx, i64> {
142+
let this = self.eval_context_mut();
143+
144+
if !this.machine.communicate {
145+
throw_unsup_format!("`open` not available when isolation is enabled")
146+
}
147+
148+
let tcx = &{ this.tcx.tcx };
149+
150+
let fd = this.read_scalar(fd_op)?.to_i32()?;
151+
let buf = this.force_ptr(this.read_scalar(buf_op)?.not_undef()?)?;
152+
let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
153+
154+
if let Some(file) = this.machine.file_handler.files.get_mut(&fd) {
155+
let mut bytes = vec![0; count as usize];
156+
match file.read(&mut bytes) {
157+
Ok(read_bytes) => {
158+
bytes.truncate(read_bytes);
159+
160+
this.memory_mut()
161+
.get_mut(buf.alloc_id)?
162+
.write_bytes(tcx, buf, &bytes)?;
163+
164+
Ok(read_bytes as i64)
165+
}
166+
Err(e) => {
167+
this.machine.last_error = e.raw_os_error().unwrap() as u32;
168+
Ok(-1)
169+
}
170+
}
171+
} else {
172+
this.machine.last_error = this
173+
.eval_path_scalar(&["libc", "EBADF"])?
174+
.unwrap()
175+
.to_u32()?;
176+
Ok(-1)
177+
}
178+
}
179+
}

src/shims/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub mod intrinsics;
33
pub mod tls;
44
pub mod dlsym;
55
pub mod env;
6+
pub mod io;
67

78
use rustc::{ty, mir};
89

tests/hello.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello, World!

tests/run-pass/file_read.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// ignore-windows: File handling is not implemented yet
2+
// compile-flags: -Zmiri-disable-isolation
3+
4+
use std::fs::File;
5+
use std::io::Read;
6+
7+
fn main() {
8+
let mut file = File::open("./tests/hello.txt").unwrap();
9+
let mut contents = String::new();
10+
file.read_to_string(&mut contents).unwrap();
11+
assert_eq!("Hello, World!\n", contents);
12+
}

0 commit comments

Comments
 (0)