Auto merge of #1263 - RalfJung:shims-limit, r=RalfJung

Limit shims to libstd where possible

Also organize them better by category.
Fixes https://github.com/rust-lang/miri/issues/1181 (by making mmap not callable from user code)
This commit is contained in:
bors 2020-03-28 10:35:11 +00:00
commit f0afdc35d2
6 changed files with 201 additions and 199 deletions

View File

@ -81,6 +81,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
/// Helper function to get a `libc` constant as an `i32`.
fn eval_libc_i32(&mut self, name: &str) -> InterpResult<'tcx, i32> {
// TODO: Cache the result.
self.eval_libc(name)?.to_i32()
}

View File

@ -196,6 +196,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
// Here we dispatch all the shims for foreign functions. If you have a platform specific
// shim, add it to the corresponding submodule.
match link_name {
// Standard C allocation
"malloc" => {
let size = this.read_scalar(args[0])?.to_machine_usize(this)?;
let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C);
@ -220,6 +221,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_scalar(res, dest)?;
}
// Rust allocation
// (Usually these would be forwarded to to `#[global_allocator]`; we instead implement a generic
// allocation that also checks that all conditions are met, such as not permitting zero-sized allocations.)
"__rust_alloc" => {
let size = this.read_scalar(args[0])?.to_machine_usize(this)?;
let align = this.read_scalar(args[1])?.to_machine_usize(this)?;
@ -274,6 +278,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_scalar(new_ptr, dest)?;
}
// C memory handling functions
"memcmp" => {
let left = this.read_scalar(args[0])?.not_undef()?;
let right = this.read_scalar(args[1])?.not_undef()?;
@ -293,7 +298,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_scalar(Scalar::from_int(result, Size::from_bits(32)), dest)?;
}
"memrchr" => {
let ptr = this.read_scalar(args[0])?.not_undef()?;
let val = this.read_scalar(args[1])?.to_i32()? as u8;
@ -311,7 +315,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_null(dest)?;
}
}
"memchr" => {
let ptr = this.read_scalar(args[0])?.not_undef()?;
let val = this.read_scalar(args[1])?.to_i32()? as u8;
@ -328,7 +331,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_null(dest)?;
}
}
"strlen" => {
let ptr = this.read_scalar(args[0])?.not_undef()?;
let n = this.memory.read_c_str(ptr)?.len();
@ -358,11 +360,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
};
this.write_scalar(Scalar::from_u32(f.to_bits()), dest)?;
}
// underscore case for windows
| "_hypotf"
| "hypotf"
| "atan2f"
=> {
// underscore case for windows, here and below
// (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
// FIXME: Using host floats.
let f1 = f32::from_bits(this.read_scalar(args[0])?.to_u32()?);
let f2 = f32::from_bits(this.read_scalar(args[1])?.to_u32()?);
@ -373,7 +376,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
};
this.write_scalar(Scalar::from_u32(n.to_bits()), dest)?;
}
| "cbrt"
| "cosh"
| "sinh"
@ -396,8 +398,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
};
this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?;
}
// underscore case for windows, here and below
// (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
| "_hypot"
| "hypot"
| "atan2"
@ -412,11 +412,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
};
this.write_scalar(Scalar::from_u64(n.to_bits()), dest)?;
}
// For radix-2 (binary) systems, `ldexp` and `scalbn` are the same.
| "_ldexp"
| "ldexp"
| "scalbn"
=> {
// For radix-2 (binary) systems, `ldexp` and `scalbn` are the same.
let x = this.read_scalar(args[0])?.to_f64()?;
let exp = this.read_scalar(args[1])?.to_i32()?;
@ -434,6 +434,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_scalar(Scalar::from_f64(res), dest)?;
}
// Target-specific shims
_ => match this.tcx.sess.target.target.target_os.as_str() {
"linux" | "macos" => return posix::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret),
"windows" => return windows::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret),

View File

@ -24,22 +24,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let result = this.getenv(args[0])?;
this.write_scalar(result, dest)?;
}
"unsetenv" => {
let result = this.unsetenv(args[0])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"setenv" => {
let result = this.setenv(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"getcwd" => {
let result = this.getcwd(args[0], args[1])?;
this.write_scalar(result, dest)?;
}
"chdir" => {
let result = this.chdir(args[0])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
@ -50,17 +46,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let result = this.open(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"fcntl" => {
let result = this.fcntl(args[0], args[1], args.get(2).cloned())?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"read" => {
let result = this.read(args[0], args[1], args[2])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"write" => {
let fd = this.read_scalar(args[0])?.to_i32()?;
let buf = this.read_scalar(args[1])?.not_undef()?;
@ -94,43 +87,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
// Now, `result` is the value we return back to the program.
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"unlink" => {
let result = this.unlink(args[0])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"symlink" => {
let result = this.symlink(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"rename" => {
let result = this.rename(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"mkdir" => {
let result = this.mkdir(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"rmdir" => {
let result = this.rmdir(args[0])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"closedir" => {
let result = this.closedir(args[0])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"lseek" | "lseek64" => {
let result = this.lseek64(args[0], args[1], args[2])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
// Other shims
// Allocation
"posix_memalign" => {
let ret = this.deref_operand(args[0])?;
let align = this.read_scalar(args[1])?.to_machine_usize(this)?;
@ -159,6 +145,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_null(dest)?;
}
// Dynamic symbol loading
"dlsym" => {
let _handle = this.read_scalar(args[0])?;
let symbol = this.read_scalar(args[1])?.not_undef()?;
@ -173,7 +160,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}
}
// Hook pthread calls that go to the thread-local storage memory subsystem.
// Querying system information
"sysconf" => {
let name = this.read_scalar(args[0])?.to_i32()?;
let sysconfs = &[
("_SC_PAGESIZE", Scalar::from_int(PAGE_SIZE, dest.layout.size)),
("_SC_NPROCESSORS_ONLN", Scalar::from_int(NUM_CPUS, dest.layout.size)),
];
let mut result = None;
for &(sysconf_name, value) in sysconfs {
let sysconf_name = this.eval_libc_i32(sysconf_name)?;
if sysconf_name == name {
result = Some(value);
break;
}
}
if let Some(result) = result {
this.write_scalar(result, dest)?;
} else {
throw_unsup_format!("unimplemented sysconf name: {}", name)
}
}
// Thread-local storage
"pthread_key_create" => {
let key_place = this.deref_operand(args[0])?;
@ -220,36 +230,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_null(dest)?;
}
// Stack size/address stuff.
| "pthread_attr_init"
| "pthread_attr_destroy"
| "pthread_self"
| "pthread_attr_setstacksize" => {
this.write_null(dest)?;
}
"pthread_attr_getstack" => {
let addr_place = this.deref_operand(args[1])?;
let size_place = this.deref_operand(args[2])?;
this.write_scalar(
Scalar::from_uint(STACK_ADDR, addr_place.layout.size),
addr_place.into(),
)?;
this.write_scalar(
Scalar::from_uint(STACK_SIZE, size_place.layout.size),
size_place.into(),
)?;
// Return success (`0`).
this.write_null(dest)?;
}
// We don't support threading.
// Better error for attempts to create a thread
"pthread_create" => {
throw_unsup_format!("Miri does not support threading");
}
// Stub out calls for condvar, mutex and rwlock, to just return `0`.
// Miscellaneous
"isatty" => {
let _fd = this.read_scalar(args[0])?.to_i32()?;
// "returns 1 if fd is an open file descriptor referring to a terminal; otherwise 0 is returned, and errno is set to indicate the error"
// FIXME: we just say nothing is a terminal.
let enotty = this.eval_libc("ENOTTY")?;
this.set_last_error(enotty)?;
this.write_null(dest)?;
}
"pthread_atfork" => {
let _prepare = this.read_scalar(args[0])?.not_undef()?;
let _parent = this.read_scalar(args[1])?.not_undef()?;
let _child = this.read_scalar(args[1])?.not_undef()?;
// We do not support forking, so there is nothing to do here.
this.write_null(dest)?;
}
// Incomplete shims that we "stub out" just to get pre-main initialziation code to work.
// These shims are enabled only when the caller is in the standard library.
| "pthread_attr_init"
| "pthread_attr_destroy"
| "pthread_self"
| "pthread_attr_setstacksize" if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
this.write_null(dest)?;
}
| "pthread_mutexattr_init"
| "pthread_mutexattr_settype"
| "pthread_mutex_init"
@ -265,68 +275,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
| "pthread_condattr_setclock"
| "pthread_cond_init"
| "pthread_condattr_destroy"
| "pthread_cond_destroy"
| "pthread_cond_destroy" if this.frame().instance.to_string().starts_with("std::sys::unix::")
=> {
this.write_null(dest)?;
}
// We don't support fork so we don't have to do anything for atfork.
"pthread_atfork" => {
this.write_null(dest)?;
}
// Some things needed for `sys::thread` initialization to go through.
| "signal"
| "sigaction"
| "sigaltstack"
| "mprotect" if this.frame().instance.to_string().starts_with("std::sys::unix::")
=> {
this.write_scalar(Scalar::from_int(0, dest.layout.size), dest)?;
}
"sysconf" => {
let name = this.read_scalar(args[0])?.to_i32()?;
trace!("sysconf() called with name {}", name);
// TODO: Cache the sysconf integers via Miri's global cache.
let sysconfs = &[
("_SC_PAGESIZE", Scalar::from_int(PAGE_SIZE, dest.layout.size)),
("_SC_GETPW_R_SIZE_MAX", Scalar::from_int(-1, dest.layout.size)),
("_SC_NPROCESSORS_ONLN", Scalar::from_int(NUM_CPUS, dest.layout.size)),
];
let mut result = None;
for &(sysconf_name, value) in sysconfs {
let sysconf_name = this.eval_libc_i32(sysconf_name)?;
if sysconf_name == name {
result = Some(value);
break;
}
}
if let Some(result) = result {
this.write_scalar(result, dest)?;
} else {
throw_unsup_format!("unimplemented sysconf name: {}", name)
}
}
"isatty" => {
this.write_null(dest)?;
}
"posix_fadvise" => {
// fadvise is only informational, we can ignore it.
this.write_null(dest)?;
}
"mmap" => {
// This is a horrible hack, but since the guard page mechanism calls mmap and expects a particular return value, we just give it that value.
let addr = this.read_scalar(args[0])?.not_undef()?;
this.write_scalar(addr, dest)?;
}
"mprotect" => {
this.write_null(dest)?;
}
// Platform-specific shims
_ => {
match this.tcx.sess.target.target.target_os.as_str() {
"linux" => return linux::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret),

View File

@ -13,47 +13,65 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let this = self.eval_context_mut();
match link_name {
// errno
"__errno_location" => {
let errno_place = this.machine.last_error.unwrap();
this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?;
}
// File related shims (but also see "syscall" below for statx)
// The only reason this is not in the `posix` module is because the `macos` item has a
// different name.
// These symbols have different names on Linux and macOS, which is the only reason they are not
// in the `posix` module.
"close" => {
let result = this.close(args[0])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
// The only reason this is not in the `posix` module is because the `macos` item has a
// different name.
"opendir" => {
let result = this.opendir(args[0])?;
this.write_scalar(result, dest)?;
}
// The `macos` module has a parallel foreign item, `readdir_r`, which uses a different
// struct layout.
"readdir64_r" => {
let result = this.linux_readdir64_r(args[0], args[1], args[2])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
// Linux-only
"posix_fadvise" => {
let _fd = this.read_scalar(args[0])?.to_i32()?;
let _offset = this.read_scalar(args[1])?.to_machine_isize(this)?;
let _len = this.read_scalar(args[2])?.to_machine_isize(this)?;
let _advice = this.read_scalar(args[3])?.to_i32()?;
// fadvise is only informational, we can ignore it.
this.write_null(dest)?;
}
// Time related shims
// This is a POSIX function but it has only been tested on linux.
"clock_gettime" => {
// This is a POSIX function but it has only been tested on linux.
let result = this.clock_gettime(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
// Other shims
"pthread_getattr_np" => {
// Querying system information
"pthread_attr_getstack" => {
// We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here.
let _attr_place = this.deref_operand(args[0])?;
let addr_place = this.deref_operand(args[1])?;
let size_place = this.deref_operand(args[2])?;
this.write_scalar(
Scalar::from_uint(STACK_ADDR, addr_place.layout.size),
addr_place.into(),
)?;
this.write_scalar(
Scalar::from_uint(STACK_SIZE, size_place.layout.size),
size_place.into(),
)?;
// Return success (`0`).
this.write_null(dest)?;
}
// Dynamically invoked syscalls
"syscall" => {
let sys_getrandom = this
.eval_libc("SYS_getrandom")?
@ -67,15 +85,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
// `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
// is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
id if id == sys_getrandom => {
// The first argument is the syscall id,
// so skip over it.
// The first argument is the syscall id, so skip over it.
getrandom(this, &args[1..], dest)?;
}
// `statx` is used by `libstd` to retrieve metadata information on `linux`
// instead of using `stat`,`lstat` or `fstat` as on `macos`.
id if id == sys_statx => {
// The first argument is the syscall id,
// so skip over it.
// The first argument is the syscall id, so skip over it.
let result = this.linux_statx(args[1], args[2], args[3], args[4], args[5])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
@ -83,15 +99,26 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}
}
// Miscelanneous
"getrandom" => {
getrandom(this, args, dest)?;
}
"sched_getaffinity" => {
// Return an error; `num_cpus` then falls back to `sysconf`.
let _pid = this.read_scalar(args[0])?.to_i32()?;
let _cpusetsize = this.read_scalar(args[1])?.to_machine_usize(this)?;
let _mask = this.deref_operand(args[2])?;
// FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
let einval = this.eval_libc("EINVAL")?;
this.set_last_error(einval)?;
this.write_scalar(Scalar::from_int(-1, dest.layout.size), dest)?;
}
// Incomplete shims that we "stub out" just to get pre-main initialziation code to work.
// These shims are enabled only when the caller is in the standard library.
"pthread_getattr_np" if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
this.write_null(dest)?;
}
_ => throw_unsup_format!("can't call foreign function: {}", link_name),
};

View File

@ -13,44 +13,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let this = self.eval_context_mut();
match link_name {
// errno
"__error" => {
let errno_place = this.machine.last_error.unwrap();
this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?;
}
// File related shims
// The only reason this is not in the `posix` module is because the `linux` item has a
// different name.
"close$NOCANCEL" => {
let result = this.close(args[0])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"stat$INODE64" => {
let result = this.macos_stat(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"lstat$INODE64" => {
let result = this.macos_lstat(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
"fstat$INODE64" => {
let result = this.macos_fstat(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
// The only reason this is not in the `posix` module is because the `linux` item has a
// different name.
"opendir$INODE64" => {
let result = this.opendir(args[0])?;
this.write_scalar(result, dest)?;
}
// The `linux` module has a parallel foreign item, `readdir64_r`, which uses a
// different struct layout.
"readdir_r$INODE64" => {
let result = this.macos_readdir_r(args[0], args[1], args[2])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
@ -71,21 +60,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_scalar(Scalar::from_uint(result, dest.layout.size), dest)?;
}
// Other shims
"pthread_attr_get_np" => {
this.write_null(dest)?;
}
"pthread_get_stackaddr_np" => {
let stack_addr = Scalar::from_uint(STACK_ADDR, dest.layout.size);
this.write_scalar(stack_addr, dest)?;
}
"pthread_get_stacksize_np" => {
let stack_size = Scalar::from_uint(STACK_SIZE, dest.layout.size);
this.write_scalar(stack_size, dest)?;
// Access to command-line arguments
"_NSGetArgc" => {
this.write_scalar(this.machine.argc.expect("machine must be initialized"), dest)?;
}
"_NSGetArgv" => {
this.write_scalar(this.machine.argv.expect("machine must be initialized"), dest)?;
}
// Thread-local storage
"_tlv_atexit" => {
let dtor = this.read_scalar(args[0])?.not_undef()?;
let dtor = this.memory.get_fn(dtor)?.as_instance()?;
@ -93,12 +76,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.machine.tls.set_global_dtor(dtor, data)?;
}
"_NSGetArgc" => {
this.write_scalar(this.machine.argc.expect("machine must be initialized"), dest)?;
// Querying system information
"pthread_get_stackaddr_np" => {
let _thread = this.read_scalar(args[0])?.not_undef()?;
let stack_addr = Scalar::from_uint(STACK_ADDR, dest.layout.size);
this.write_scalar(stack_addr, dest)?;
}
"pthread_get_stacksize_np" => {
let _thread = this.read_scalar(args[0])?.not_undef()?;
let stack_size = Scalar::from_uint(STACK_SIZE, dest.layout.size);
this.write_scalar(stack_size, dest)?;
}
"_NSGetArgv" => {
this.write_scalar(this.machine.argv.expect("machine must be initialized"), dest)?;
// Incomplete shims that we "stub out" just to get pre-main initialziation code to work.
// These shims are enabled only when the caller is in the standard library.
"mmap" if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
// This is a horrible hack, but since the guard page mechanism calls mmap and expects a particular return value, we just give it that value.
let addr = this.read_scalar(args[0])?.not_undef()?;
this.write_scalar(addr, dest)?;
}
_ => throw_unsup_format!("can't call foreign function: {}", link_name),

View File

@ -14,12 +14,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
) -> InterpResult<'tcx, bool> {
let this = self.eval_context_mut();
// Windows API stubs.
// HANDLE = isize
// DWORD = ULONG = u32
// BOOL = i32
match link_name {
// Windows API stubs.
// HANDLE = isize
// DWORD = ULONG = u32
// BOOL = i32
// Environment related shims
"GetEnvironmentVariableW" => {
let result = this.GetEnvironmentVariableW(args[0], args[1], args[2])?;
@ -42,6 +41,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}
// File related shims
"GetStdHandle" => {
let which = this.read_scalar(args[0])?.to_i32()?;
// We just make this the identity function, so we know later in `WriteFile`
// which one it is.
this.write_scalar(Scalar::from_int(which, this.pointer_size()), dest)?;
}
"WriteFile" => {
let handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
let buf = this.read_scalar(args[1])?.not_undef()?;
@ -61,9 +66,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
};
res.ok().map(|n| n as u32)
} else {
eprintln!("Miri: Ignored output to handle {}", handle);
// Pretend it all went well.
Some(n)
throw_unsup_format!("on Windows, writing to anything except stdout/stderr is not supported")
};
// If there was no error, write back how much was written.
if let Some(n) = written {
@ -76,11 +79,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
)?;
}
// Other shims
"GetProcessHeap" => {
// Just fake a HANDLE
this.write_scalar(Scalar::from_int(1, this.pointer_size()), dest)?;
}
// Allocation
"HeapAlloc" => {
let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
let flags = this.read_scalar(args[1])?.to_u32()?;
@ -105,6 +104,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_scalar(res, dest)?;
}
// errno
"SetLastError" => {
this.set_last_error(this.read_scalar(args[0])?.not_undef()?)?;
}
@ -113,30 +113,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_scalar(last_error, dest)?;
}
"AddVectoredExceptionHandler" => {
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
this.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
}
| "InitializeCriticalSection"
| "EnterCriticalSection"
| "LeaveCriticalSection"
| "DeleteCriticalSection"
=> {
// Nothing to do, not even a return value.
// (Windows locks are reentrant, and we have only 1 thread,
// so not doing any futher checks here is at least not incorrect.)
}
| "GetModuleHandleW"
| "GetProcAddress"
| "GetConsoleScreenBufferInfo"
| "SetConsoleTextAttribute"
=> {
// Pretend these do not exist / nothing happened, by returning zero.
this.write_null(dest)?;
}
// Querying system information
"GetSystemInfo" => {
let system_info = this.deref_operand(args[0])?;
// Initialize with `0`.
@ -150,6 +127,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_scalar(Scalar::from_int(NUM_CPUS, dword_size), num_cpus.into())?;
}
// Thread-local storage
"TlsAlloc" => {
// This just creates a key; Windows does not natively support TLS destructors.
@ -170,33 +148,72 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
// Return success (`1`).
this.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
}
"GetStdHandle" => {
let which = this.read_scalar(args[0])?.to_i32()?;
// We just make this the identity function, so we know later in `WriteFile`
// which one it is.
this.write_scalar(Scalar::from_int(which, this.pointer_size()), dest)?;
}
"GetConsoleMode" => {
// Everything is a pipe.
this.write_null(dest)?;
}
// Access to command-line arguments
"GetCommandLineW" => {
this.write_scalar(
this.machine.cmd_line.expect("machine must be initialized"),
dest,
)?;
}
// The actual name of 'RtlGenRandom'
// Miscellaneous
"SystemFunction036" => {
// The actual name of 'RtlGenRandom'
let ptr = this.read_scalar(args[0])?.not_undef()?;
let len = this.read_scalar(args[1])?.to_u32()?;
this.gen_random(ptr, len.into())?;
this.write_scalar(Scalar::from_bool(true), dest)?;
}
// We don't support threading.
"GetConsoleScreenBufferInfo" => {
// `term` needs this, so we fake it.
let _console = this.read_scalar(args[0])?.to_machine_isize(this)?;
let _buffer_info = this.deref_operand(args[1])?;
// Indicate an error.
// FIXME: we should set last_error, but to what?
this.write_null(dest)?;
}
"GetConsoleMode" => {
// Windows "isatty" (in libtest) needs this, so we fake it.
let _console = this.read_scalar(args[0])?.to_machine_isize(this)?;
let _mode = this.deref_operand(args[1])?;
// Indicate an error.
// FIXME: we should set last_error, but to what?
this.write_null(dest)?;
}
// Better error for attempts to create a thread
"CreateThread" => {
throw_unsup_format!("Miri does not support threading");
}
// Incomplete shims that we "stub out" just to get pre-main initialziation code to work.
// These shims are enabled only when the caller is in the standard library.
"GetProcessHeap" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
// Just fake a HANDLE
this.write_scalar(Scalar::from_int(1, this.pointer_size()), dest)?;
}
| "GetModuleHandleW"
| "GetProcAddress"
| "SetConsoleTextAttribute" if this.frame().instance.to_string().starts_with("std::sys::windows::")
=> {
// Pretend these do not exist / nothing happened, by returning zero.
this.write_null(dest)?;
}
"AddVectoredExceptionHandler" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
this.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
}
| "InitializeCriticalSection"
| "EnterCriticalSection"
| "LeaveCriticalSection"
| "DeleteCriticalSection" if this.frame().instance.to_string().starts_with("std::sys::windows::")
=> {
// Nothing to do, not even a return value.
// (Windows locks are reentrant, and we have only 1 thread,
// so not doing any futher checks here is at least not incorrect.)
}
_ => throw_unsup_format!("can't call foreign function: {}", link_name),
}