Skip to content

Commit 2b2a3a0

Browse files
committed
check that all syscall arguments are scalars
1 parent 5620ad0 commit 2b2a3a0

File tree

3 files changed

+23
-21
lines changed

3 files changed

+23
-21
lines changed

src/bin/miri.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ fn main() {
221221
),
222222
FromHexError::OddLength =>
223223
panic!("-Zmiri-seed should have an even number of digits"),
224-
err => panic!("Unknown error decoding -Zmiri-seed as hex: {:?}", err),
224+
err => panic!("unknown error decoding -Zmiri-seed as hex: {:?}", err),
225225
});
226226
if seed_raw.len() > 8 {
227227
panic!(format!(

src/shims/posix/linux/foreign_items.rs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -113,16 +113,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
113113

114114
// Dynamically invoked syscalls
115115
"syscall" => {
116-
// FIXME: The libc syscall() function is a variadic function.
117-
// It's valid to call it with more arguments than a syscall
118-
// needs, so none of these syscalls should use check_arg_count.
119-
// It's even valid to call it with the wrong type of arguments,
120-
// as long as they'd end up in the same place with the calling
121-
// convention used. (E.g. using a `usize` instead of a pointer.)
122-
// It's not directly clear which number, size, and type of arguments
123-
// are acceptable in which cases and which aren't. (E.g. some
124-
// types might take up the space of two registers.)
125-
// So this needs to be researched first.
116+
// The syscall variadic function is legal to call with more arguments than needed,
117+
// extra arguments are simply ignored. However, all arguments need to be scalars;
118+
// other types might be treated differently by the calling convention.
119+
for arg in args {
120+
if !matches!(arg.layout.abi, rustc_target::abi::Abi::Scalar(_)) {
121+
throw_ub_format!("`syscall` arguments must all have scalar layout, but {} does not", arg.layout.ty);
122+
}
123+
}
126124

127125
let sys_getrandom = this
128126
.eval_libc("SYS_getrandom")?
@@ -144,22 +142,26 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
144142
// is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
145143
id if id == sys_getrandom => {
146144
// The first argument is the syscall id, so skip over it.
147-
let &[_, ptr, len, flags] = check_arg_count(args)?;
148-
getrandom(this, ptr, len, flags, dest)?;
145+
if args.len() < 4 {
146+
throw_ub_format!("incorrect number of arguments for `getrandom` syscall: got {}, expected at least 4", args.len());
147+
}
148+
getrandom(this, args[1], args[2], args[3], dest)?;
149149
}
150150
// `statx` is used by `libstd` to retrieve metadata information on `linux`
151151
// instead of using `stat`,`lstat` or `fstat` as on `macos`.
152152
id if id == sys_statx => {
153153
// The first argument is the syscall id, so skip over it.
154-
let &[_, dirfd, pathname, flags, mask, statxbuf] = check_arg_count(args)?;
155-
let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
154+
if args.len() < 6 {
155+
throw_ub_format!("incorrect number of arguments for `statx` syscall: got {}, expected at least 6", args.len());
156+
}
157+
let result = this.linux_statx(args[1], args[2], args[3], args[4], args[5])?;
156158
this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
157159
}
158160
// `futex` is used by some synchonization primitives.
159161
id if id == sys_futex => {
160162
futex(this, args, dest)?;
161163
}
162-
id => throw_unsup_format!("miri does not support syscall ID {}", id),
164+
id => throw_unsup_format!("Miri does not support syscall ID {}", id),
163165
}
164166
}
165167

src/shims/posix/linux/sync.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ pub fn futex<'tcx>(
1717
// may or may not be left out from the `syscall()` call.
1818
// Therefore we don't use `check_arg_count` here, but only check for the
1919
// number of arguments to fall within a range.
20-
if !(4..=7).contains(&args.len()) {
21-
throw_ub_format!("incorrect number of arguments for futex syscall: got {}, expected between 4 and 7 (inclusive)", args.len());
20+
if args.len() < 4 {
21+
throw_ub_format!("incorrect number of arguments for `futex` syscall: got {}, expected at least 4", args.len());
2222
}
2323

2424
// The first three arguments (after the syscall number itself) are the same to all futex operations:
@@ -49,13 +49,13 @@ pub fn futex<'tcx>(
4949
// or *timeout expires. `timeout == null` for an infinite timeout.
5050
op if op & !futex_realtime == futex_wait => {
5151
if args.len() < 5 {
52-
throw_ub_format!("incorrect number of arguments for FUTEX_WAIT syscall: got {}, expected at least 5", args.len());
52+
throw_ub_format!("incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT`: got {}, expected at least 5", args.len());
5353
}
5454
let timeout = args[4];
5555
let timeout_time = if this.is_null(this.read_scalar(timeout)?.check_init()?)? {
5656
None
5757
} else {
58-
this.check_no_isolation("`syscall(SYS_FUTEX, op=FUTEX_WAIT)` with timeout")?;
58+
this.check_no_isolation("`futex` syscall with `op=FUTEX_WAIT` and non-null timeout")?;
5959
let duration = match this.read_timespec(timeout)? {
6060
Some(duration) => duration,
6161
None => {
@@ -126,7 +126,7 @@ pub fn futex<'tcx>(
126126
}
127127
this.write_scalar(Scalar::from_machine_isize(n, this), dest)?;
128128
}
129-
op => throw_unsup_format!("miri does not support SYS_futex operation {}", op),
129+
op => throw_unsup_format!("Miri does not support `futex` syscall with op={}", op),
130130
}
131131

132132
Ok(())

0 commit comments

Comments
 (0)