Auto merge of #103571 - RalfJung:miri, r=RalfJung
update Miri r? `@ghost`
This commit is contained in:
commit
1898c34e92
@ -538,15 +538,23 @@ extern "Rust" {
|
||||
fn miri_start_panic(payload: *mut u8) -> !;
|
||||
|
||||
/// Miri-provided extern function to get the internal unique identifier for the allocation that a pointer
|
||||
/// points to. This is only useful as an input to `miri_print_stacks`, and it is a separate call because
|
||||
/// points to. If this pointer is invalid (not pointing to an allocation), interpretation will abort.
|
||||
///
|
||||
/// This is only useful as an input to `miri_print_borrow_stacks`, and it is a separate call because
|
||||
/// getting a pointer to an allocation at runtime can change the borrow stacks in the allocation.
|
||||
/// This function should be considered unstable. It exists only to support `miri_print_borrow_stacks` and so
|
||||
/// inherits all of its instability.
|
||||
fn miri_get_alloc_id(ptr: *const ()) -> u64;
|
||||
|
||||
/// Miri-provided extern function to print (from the interpreter, not the program) the contents of all
|
||||
/// borrow stacks in an allocation. The format of what this emits is unstable and may change at any time.
|
||||
/// In particular, users should be aware that Miri will periodically attempt to garbage collect the
|
||||
/// contents of all stacks. Callers of this function may wish to pass `-Zmiri-tag-gc=0` to disable the GC.
|
||||
fn miri_print_stacks(alloc_id: u64);
|
||||
/// borrow stacks in an allocation. The leftmost tag is the bottom of the stack.
|
||||
/// The format of what this emits is unstable and may change at any time. In particular, users should be
|
||||
/// aware that Miri will periodically attempt to garbage collect the contents of all stacks. Callers of
|
||||
/// this function may wish to pass `-Zmiri-tag-gc=0` to disable the GC.
|
||||
///
|
||||
/// This function is extremely unstable. At any time the format of its output may change, its signature may
|
||||
/// change, or it may be removed entirely.
|
||||
fn miri_print_borrow_stacks(alloc_id: u64);
|
||||
|
||||
/// Miri-provided extern function to print (from the interpreter, not the
|
||||
/// program) the contents of a section of program memory, as bytes. Bytes
|
||||
|
@ -4,5 +4,5 @@ fn main() {
|
||||
// Re-export the TARGET environment variable so it can
|
||||
// be accessed by miri.
|
||||
let target = std::env::var("TARGET").unwrap();
|
||||
println!("cargo:rustc-env=TARGET={}", target);
|
||||
println!("cargo:rustc-env=TARGET={target}");
|
||||
}
|
||||
|
@ -34,7 +34,7 @@
|
||||
"#;
|
||||
|
||||
fn show_help() {
|
||||
println!("{}", CARGO_MIRI_HELP);
|
||||
println!("{CARGO_MIRI_HELP}");
|
||||
}
|
||||
|
||||
fn show_version() {
|
||||
@ -52,7 +52,7 @@ fn forward_patched_extern_arg(args: &mut impl Iterator<Item = String>, cmd: &mut
|
||||
let path = args.next().expect("`--extern` should be followed by a filename");
|
||||
if let Some(lib) = path.strip_suffix(".rlib") {
|
||||
// If this is an rlib, make it an rmeta.
|
||||
cmd.arg(format!("{}.rmeta", lib));
|
||||
cmd.arg(format!("{lib}.rmeta"));
|
||||
} else {
|
||||
// Some other extern file (e.g. a `.so`). Forward unchanged.
|
||||
cmd.arg(path);
|
||||
@ -336,7 +336,7 @@ fn out_filename(prefix: &str, suffix: &str) -> PathBuf {
|
||||
"[cargo-miri rustc inside rustdoc] captured input:\n{}",
|
||||
std::str::from_utf8(&env.stdin).unwrap()
|
||||
);
|
||||
eprintln!("[cargo-miri rustc inside rustdoc] going to run:\n{:?}", cmd);
|
||||
eprintln!("[cargo-miri rustc inside rustdoc] going to run:\n{cmd:?}");
|
||||
}
|
||||
|
||||
exec_with_pipe(cmd, &env.stdin, format!("{}.stdin", out_filename("", "").display()));
|
||||
@ -374,7 +374,7 @@ fn out_filename(prefix: &str, suffix: &str) -> PathBuf {
|
||||
val.push("metadata");
|
||||
}
|
||||
}
|
||||
cmd.arg(format!("{}={}", emit_flag, val.join(",")));
|
||||
cmd.arg(format!("{emit_flag}={}", val.join(",")));
|
||||
} else if arg == "--extern" {
|
||||
// Patch `--extern` filenames, since Cargo sometimes passes stub `.rlib` files:
|
||||
// https://github.com/rust-lang/miri/issues/1705
|
||||
@ -535,7 +535,7 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
|
||||
// Run it.
|
||||
debug_cmd("[cargo-miri runner]", verbose, &cmd);
|
||||
match phase {
|
||||
RunnerPhase::Rustdoc => exec_with_pipe(cmd, &info.stdin, format!("{}.stdin", binary)),
|
||||
RunnerPhase::Rustdoc => exec_with_pipe(cmd, &info.stdin, format!("{binary}.stdin")),
|
||||
RunnerPhase::Cargo => exec(cmd),
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ pub fn escape_for_toml(s: &str) -> String {
|
||||
// We want to surround this string in quotes `"`. So we first escape all quotes,
|
||||
// and also all backslashes (that are used to escape quotes).
|
||||
let s = s.replace('\\', r#"\\"#).replace('"', r#"\""#);
|
||||
format!("\"{}\"", s)
|
||||
format!("\"{s}\"")
|
||||
}
|
||||
|
||||
/// Returns the path to the `miri` binary
|
||||
@ -175,7 +175,7 @@ pub fn ask_to_run(mut cmd: Command, ask: bool, text: &str) {
|
||||
let is_ci = env::var_os("CI").is_some() || env::var_os("TF_BUILD").is_some();
|
||||
if ask && !is_ci {
|
||||
let mut buf = String::new();
|
||||
print!("I will run `{:?}` to {}. Proceed? [Y/n] ", cmd, text);
|
||||
print!("I will run `{cmd:?}` to {text}. Proceed? [Y/n] ");
|
||||
io::stdout().flush().unwrap();
|
||||
io::stdin().read_line(&mut buf).unwrap();
|
||||
match buf.trim().to_lowercase().as_ref() {
|
||||
@ -185,10 +185,10 @@ pub fn ask_to_run(mut cmd: Command, ask: bool, text: &str) {
|
||||
a => show_error!("invalid answer `{}`", a),
|
||||
};
|
||||
} else {
|
||||
eprintln!("Running `{:?}` to {}.", cmd, text);
|
||||
eprintln!("Running `{cmd:?}` to {text}.");
|
||||
}
|
||||
|
||||
if cmd.status().unwrap_or_else(|_| panic!("failed to execute {:?}", cmd)).success().not() {
|
||||
if cmd.status().unwrap_or_else(|_| panic!("failed to execute {cmd:?}")).success().not() {
|
||||
show_error!("failed to {}", text);
|
||||
}
|
||||
}
|
||||
@ -276,12 +276,12 @@ pub fn debug_cmd(prefix: &str, verbose: usize, cmd: &Command) {
|
||||
// Print only what has been changed for this `cmd`.
|
||||
for (var, val) in cmd.get_envs() {
|
||||
if let Some(val) = val {
|
||||
writeln!(out, "{}={:?} \\", var.to_string_lossy(), val).unwrap();
|
||||
writeln!(out, "{}={val:?} \\", var.to_string_lossy()).unwrap();
|
||||
} else {
|
||||
writeln!(out, "--unset={}", var.to_string_lossy()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(out, "{cmd:?}").unwrap();
|
||||
eprintln!("{}", out);
|
||||
eprintln!("{out}");
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
b1ab3b738ac718da74cd4aa0bb7f362d0adbdf84
|
||||
85d089b41e2a0c0f07ab34f6c5a7c451389f25e6
|
||||
|
@ -192,7 +192,7 @@ fn init_late_loggers(tcx: TyCtxt<'_>) {
|
||||
if log::Level::from_str(&var).is_ok() {
|
||||
env::set_var(
|
||||
"RUSTC_LOG",
|
||||
&format!(
|
||||
format!(
|
||||
"rustc_middle::mir::interpret={0},rustc_const_eval::interpret={0}",
|
||||
var
|
||||
),
|
||||
@ -243,7 +243,7 @@ fn host_sysroot() -> Option<String> {
|
||||
)
|
||||
}
|
||||
}
|
||||
format!("{}/toolchains/{}", home, toolchain)
|
||||
format!("{home}/toolchains/{toolchain}")
|
||||
}
|
||||
_ => option_env!("RUST_SYSROOT")
|
||||
.unwrap_or_else(|| {
|
||||
@ -330,7 +330,7 @@ fn main() {
|
||||
} else if crate_kind == "host" {
|
||||
false
|
||||
} else {
|
||||
panic!("invalid `MIRI_BE_RUSTC` value: {:?}", crate_kind)
|
||||
panic!("invalid `MIRI_BE_RUSTC` value: {crate_kind:?}")
|
||||
};
|
||||
|
||||
// We cannot use `rustc_driver::main` as we need to adjust the CLI arguments.
|
||||
|
@ -870,6 +870,7 @@ fn active_thread_stack_mut(
|
||||
this.machine.threads.active_thread_stack_mut()
|
||||
}
|
||||
|
||||
/// Set the name of the current thread. The buffer must not include the null terminator.
|
||||
#[inline]
|
||||
fn set_thread_name(&mut self, thread: ThreadId, new_thread_name: Vec<u8>) {
|
||||
let this = self.eval_context_mut();
|
||||
|
@ -399,7 +399,7 @@ fn assert_order(l: &[VTimestamp], r: &[VTimestamp], o: Option<Ordering>) {
|
||||
|
||||
//Test partial_cmp
|
||||
let compare = l.partial_cmp(&r);
|
||||
assert_eq!(compare, o, "Invalid comparison\n l: {:?}\n r: {:?}", l, r);
|
||||
assert_eq!(compare, o, "Invalid comparison\n l: {l:?}\n r: {r:?}");
|
||||
let alt_compare = r.partial_cmp(&l);
|
||||
assert_eq!(
|
||||
alt_compare,
|
||||
|
@ -263,7 +263,7 @@ pub fn report_error<'tcx, 'mir>(
|
||||
msg.insert(0, e.to_string());
|
||||
report_msg(
|
||||
DiagLevel::Error,
|
||||
&if let Some(title) = title { format!("{}: {}", title, msg[0]) } else { msg[0].clone() },
|
||||
&if let Some(title) = title { format!("{title}: {}", msg[0]) } else { msg[0].clone() },
|
||||
msg,
|
||||
vec![],
|
||||
helps,
|
||||
|
@ -1,6 +1,7 @@
|
||||
pub mod convert;
|
||||
|
||||
use std::cmp;
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::time::Duration;
|
||||
@ -107,7 +108,7 @@ fn try_resolve_path(&self, path: &[&str]) -> Option<ty::Instance<'tcx>> {
|
||||
/// Gets an instance for a path.
|
||||
fn resolve_path(&self, path: &[&str]) -> ty::Instance<'tcx> {
|
||||
self.try_resolve_path(path)
|
||||
.unwrap_or_else(|| panic!("failed to find required Rust item: {:?}", path))
|
||||
.unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
|
||||
}
|
||||
|
||||
/// Evaluates the scalar at the specified path. Returns Some(val)
|
||||
@ -505,7 +506,7 @@ fn reject_in_isolation(&self, op_name: &str, reject_with: RejectOpWith) -> Inter
|
||||
RejectOpWith::WarningWithoutBacktrace => {
|
||||
this.tcx
|
||||
.sess
|
||||
.warn(&format!("{} was made to return an error due to isolation", op_name));
|
||||
.warn(format!("{op_name} was made to return an error due to isolation"));
|
||||
Ok(())
|
||||
}
|
||||
RejectOpWith::Warning => {
|
||||
@ -735,6 +736,7 @@ fn read_timespec(
|
||||
})
|
||||
}
|
||||
|
||||
/// Read a sequence of bytes until the first null terminator.
|
||||
fn read_c_str<'a>(&'a self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, &'a [u8]>
|
||||
where
|
||||
'tcx: 'a,
|
||||
@ -761,6 +763,30 @@ fn read_c_str<'a>(&'a self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'t
|
||||
this.read_bytes_ptr_strip_provenance(ptr, len)
|
||||
}
|
||||
|
||||
/// Helper function to write a sequence of bytes with an added null-terminator, which is what
|
||||
/// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
|
||||
/// to write if `size` is not large enough to fit the contents of `c_str` plus a null
|
||||
/// terminator. It returns `Ok((true, length))` if the writing process was successful. The
|
||||
/// string length returned does include the null terminator.
|
||||
fn write_c_str(
|
||||
&mut self,
|
||||
c_str: &[u8],
|
||||
ptr: Pointer<Option<Provenance>>,
|
||||
size: u64,
|
||||
) -> InterpResult<'tcx, (bool, u64)> {
|
||||
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
|
||||
// terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
|
||||
let string_length = u64::try_from(c_str.len()).unwrap();
|
||||
let string_length = string_length.checked_add(1).unwrap();
|
||||
if size < string_length {
|
||||
return Ok((false, string_length));
|
||||
}
|
||||
self.eval_context_mut()
|
||||
.write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?;
|
||||
Ok((true, string_length))
|
||||
}
|
||||
|
||||
/// Read a sequence of u16 until the first null terminator.
|
||||
fn read_wide_str(&self, mut ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, Vec<u16>> {
|
||||
let this = self.eval_context_ref();
|
||||
let size2 = Size::from_bytes(2);
|
||||
@ -783,6 +809,39 @@ fn read_wide_str(&self, mut ptr: Pointer<Option<Provenance>>) -> InterpResult<'t
|
||||
Ok(wchars)
|
||||
}
|
||||
|
||||
/// Helper function to write a sequence of u16 with an added 0x0000-terminator, which is what
|
||||
/// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
|
||||
/// to write if `size` is not large enough to fit the contents of `os_string` plus a null
|
||||
/// terminator. It returns `Ok((true, length))` if the writing process was successful. The
|
||||
/// string length returned does include the null terminator. Length is measured in units of
|
||||
/// `u16.`
|
||||
fn write_wide_str(
|
||||
&mut self,
|
||||
wide_str: &[u16],
|
||||
ptr: Pointer<Option<Provenance>>,
|
||||
size: u64,
|
||||
) -> InterpResult<'tcx, (bool, u64)> {
|
||||
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required
|
||||
// 0x0000 terminator to memory would cause an out-of-bounds access.
|
||||
let string_length = u64::try_from(wide_str.len()).unwrap();
|
||||
let string_length = string_length.checked_add(1).unwrap();
|
||||
if size < string_length {
|
||||
return Ok((false, string_length));
|
||||
}
|
||||
|
||||
// Store the UTF-16 string.
|
||||
let size2 = Size::from_bytes(2);
|
||||
let this = self.eval_context_mut();
|
||||
let mut alloc = this
|
||||
.get_ptr_alloc_mut(ptr, size2 * string_length, Align::from_bytes(2).unwrap())?
|
||||
.unwrap(); // not a ZST, so we will get a result
|
||||
for (offset, wchar) in wide_str.iter().copied().chain(iter::once(0x0000)).enumerate() {
|
||||
let offset = u64::try_from(offset).unwrap();
|
||||
alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?;
|
||||
}
|
||||
Ok((true, string_length))
|
||||
}
|
||||
|
||||
/// Check that the ABI is what we expect.
|
||||
fn check_abi<'a>(&self, abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> {
|
||||
if self.eval_context_ref().machine.enforce_abi && abi != exp_abi {
|
||||
|
@ -191,12 +191,12 @@ fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Provenance::Concrete { alloc_id, sb } => {
|
||||
// Forward `alternate` flag to `alloc_id` printing.
|
||||
if f.alternate() {
|
||||
write!(f, "[{:#?}]", alloc_id)?;
|
||||
write!(f, "[{alloc_id:#?}]")?;
|
||||
} else {
|
||||
write!(f, "[{:?}]", alloc_id)?;
|
||||
write!(f, "[{alloc_id:?}]")?;
|
||||
}
|
||||
// Print Stacked Borrows tag.
|
||||
write!(f, "{:?}", sb)?;
|
||||
write!(f, "{sb:?}")?;
|
||||
}
|
||||
Provenance::Wildcard => {
|
||||
write!(f, "[wildcard]")?;
|
||||
|
@ -40,7 +40,7 @@ fn find_offset(&self, offset: u64) -> usize {
|
||||
let mut left = 0usize; // inclusive
|
||||
let mut right = self.v.len(); // exclusive
|
||||
loop {
|
||||
debug_assert!(left < right, "find_offset: offset {} is out-of-bounds", offset);
|
||||
debug_assert!(left < right, "find_offset: offset {offset} is out-of-bounds");
|
||||
let candidate = left.checked_add(right).unwrap() / 2;
|
||||
let elem = &self.v[candidate];
|
||||
if offset < elem.range.start {
|
||||
|
@ -321,7 +321,7 @@ fn emulate_foreign_item(
|
||||
return Ok(Some(body));
|
||||
}
|
||||
|
||||
this.handle_unsupported(format!("can't call foreign function: {}", link_name))?;
|
||||
this.handle_unsupported(format!("can't call foreign function: {link_name}"))?;
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
@ -420,10 +420,14 @@ fn emulate_foreign_item_by_name(
|
||||
"miri_get_alloc_id" => {
|
||||
let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr)?;
|
||||
let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr).map_err(|_e| {
|
||||
err_machine_stop!(TerminationInfo::Abort(
|
||||
format!("pointer passed to miri_get_alloc_id must not be dangling, got {ptr:?}")
|
||||
))
|
||||
})?;
|
||||
this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
|
||||
}
|
||||
"miri_print_stacks" => {
|
||||
"miri_print_borrow_stacks" => {
|
||||
let [id] = this.check_shim(abi, Abi::Rust, link_name, args)?;
|
||||
let id = this.read_scalar(id)?.to_u64()?;
|
||||
if let Some(id) = std::num::NonZeroU64::new(id) {
|
||||
|
@ -1,6 +1,5 @@
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::iter;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[cfg(unix)]
|
||||
@ -9,7 +8,6 @@
|
||||
use std::os::windows::ffi::{OsStrExt, OsStringExt};
|
||||
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_target::abi::{Align, Size};
|
||||
|
||||
use crate::*;
|
||||
|
||||
@ -100,16 +98,7 @@ fn write_os_str_to_c_str(
|
||||
size: u64,
|
||||
) -> InterpResult<'tcx, (bool, u64)> {
|
||||
let bytes = os_str_to_bytes(os_str)?;
|
||||
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
|
||||
// terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
|
||||
let string_length = u64::try_from(bytes.len()).unwrap();
|
||||
let string_length = string_length.checked_add(1).unwrap();
|
||||
if size < string_length {
|
||||
return Ok((false, string_length));
|
||||
}
|
||||
self.eval_context_mut()
|
||||
.write_bytes_ptr(ptr, bytes.iter().copied().chain(iter::once(0u8)))?;
|
||||
Ok((true, string_length))
|
||||
self.eval_context_mut().write_c_str(bytes, ptr, size)
|
||||
}
|
||||
|
||||
/// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what
|
||||
@ -140,25 +129,7 @@ fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
|
||||
}
|
||||
|
||||
let u16_vec = os_str_to_u16vec(os_str)?;
|
||||
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required
|
||||
// 0x0000 terminator to memory would cause an out-of-bounds access.
|
||||
let string_length = u64::try_from(u16_vec.len()).unwrap();
|
||||
let string_length = string_length.checked_add(1).unwrap();
|
||||
if size < string_length {
|
||||
return Ok((false, string_length));
|
||||
}
|
||||
|
||||
// Store the UTF-16 string.
|
||||
let size2 = Size::from_bytes(2);
|
||||
let this = self.eval_context_mut();
|
||||
let mut alloc = this
|
||||
.get_ptr_alloc_mut(ptr, size2 * string_length, Align::from_bytes(2).unwrap())?
|
||||
.unwrap(); // not a ZST, so we will get a result
|
||||
for (offset, wchar) in u16_vec.into_iter().chain(iter::once(0x0000)).enumerate() {
|
||||
let offset = u64::try_from(offset).unwrap();
|
||||
alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?;
|
||||
}
|
||||
Ok((true, string_length))
|
||||
self.eval_context_mut().write_wide_str(&u16_vec, ptr, size)
|
||||
}
|
||||
|
||||
/// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes.
|
||||
|
@ -26,8 +26,12 @@ fn emulate_foreign_item_by_name(
|
||||
"pthread_set_name_np" => {
|
||||
let [thread, name] =
|
||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let res =
|
||||
this.pthread_setname_np(this.read_scalar(thread)?, this.read_scalar(name)?)?;
|
||||
let max_len = usize::MAX; // freebsd does not seem to have a limit.
|
||||
let res = this.pthread_setname_np(
|
||||
this.read_scalar(thread)?,
|
||||
this.read_scalar(name)?,
|
||||
max_len,
|
||||
)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
|
@ -621,7 +621,7 @@ fn open(&mut self, args: &[OpTy<'tcx, Provenance>]) -> InterpResult<'tcx, i32> {
|
||||
return Ok(-1);
|
||||
}
|
||||
|
||||
let fd = options.open(&path).map(|file| {
|
||||
let fd = options.open(path).map(|file| {
|
||||
let fh = &mut this.machine.file_handler;
|
||||
fh.insert_fd(Box::new(FileHandle { file, writable }))
|
||||
});
|
||||
@ -1862,7 +1862,7 @@ fn mkstemp(&mut self, template_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx
|
||||
|
||||
let possibly_unique = std::env::temp_dir().join::<PathBuf>(p.into());
|
||||
|
||||
let file = fopts.open(&possibly_unique);
|
||||
let file = fopts.open(possibly_unique);
|
||||
|
||||
match file {
|
||||
Ok(f) => {
|
||||
|
@ -68,8 +68,22 @@ fn emulate_foreign_item_by_name(
|
||||
"pthread_setname_np" => {
|
||||
let [thread, name] =
|
||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let res =
|
||||
this.pthread_setname_np(this.read_scalar(thread)?, this.read_scalar(name)?)?;
|
||||
let max_len = 16;
|
||||
let res = this.pthread_setname_np(
|
||||
this.read_scalar(thread)?,
|
||||
this.read_scalar(name)?,
|
||||
max_len,
|
||||
)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"pthread_getname_np" => {
|
||||
let [thread, name, len] =
|
||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let res = this.pthread_getname_np(
|
||||
this.read_scalar(thread)?,
|
||||
this.read_scalar(name)?,
|
||||
this.read_scalar(len)?,
|
||||
)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
@ -126,7 +140,7 @@ fn emulate_foreign_item_by_name(
|
||||
futex(this, &args[1..], dest)?;
|
||||
}
|
||||
id => {
|
||||
this.handle_unsupported(format!("can't execute syscall with ID {}", id))?;
|
||||
this.handle_unsupported(format!("can't execute syscall with ID {id}"))?;
|
||||
return Ok(EmulateByNameResult::AlreadyJumped);
|
||||
}
|
||||
}
|
||||
|
@ -176,7 +176,22 @@ fn emulate_foreign_item_by_name(
|
||||
"pthread_setname_np" => {
|
||||
let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let thread = this.pthread_self()?;
|
||||
this.pthread_setname_np(thread, this.read_scalar(name)?)?;
|
||||
let max_len = this.eval_libc("MAXTHREADNAMESIZE")?.to_machine_usize(this)?;
|
||||
this.pthread_setname_np(
|
||||
thread,
|
||||
this.read_scalar(name)?,
|
||||
max_len.try_into().unwrap(),
|
||||
)?;
|
||||
}
|
||||
"pthread_getname_np" => {
|
||||
let [thread, name, len] =
|
||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let res = this.pthread_getname_np(
|
||||
this.read_scalar(thread)?,
|
||||
this.read_scalar(name)?,
|
||||
this.read_scalar(len)?,
|
||||
)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
||||
|
@ -67,10 +67,13 @@ fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
Ok(Scalar::from_machine_usize(thread_id.into(), this))
|
||||
}
|
||||
|
||||
/// Set the name of the current thread. `max_name_len` is the maximal length of the name
|
||||
/// including the null terminator.
|
||||
fn pthread_setname_np(
|
||||
&mut self,
|
||||
thread: Scalar<Provenance>,
|
||||
name: Scalar<Provenance>,
|
||||
max_name_len: usize,
|
||||
) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
@ -78,11 +81,35 @@ fn pthread_setname_np(
|
||||
let name = name.to_pointer(this)?;
|
||||
|
||||
let name = this.read_c_str(name)?.to_owned();
|
||||
|
||||
// Comparing with `>=` to account for null terminator.
|
||||
if name.len() >= max_name_len {
|
||||
return this.eval_libc("ERANGE");
|
||||
}
|
||||
|
||||
this.set_thread_name(thread, name);
|
||||
|
||||
Ok(Scalar::from_u32(0))
|
||||
}
|
||||
|
||||
fn pthread_getname_np(
|
||||
&mut self,
|
||||
thread: Scalar<Provenance>,
|
||||
name_out: Scalar<Provenance>,
|
||||
len: Scalar<Provenance>,
|
||||
) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let thread = ThreadId::try_from(thread.to_machine_usize(this)?).unwrap();
|
||||
let name_out = name_out.to_pointer(this)?;
|
||||
let len = len.to_machine_usize(this)?;
|
||||
|
||||
let name = this.get_thread_name(thread).to_owned();
|
||||
let (success, _written) = this.write_c_str(&name, name_out, len)?;
|
||||
|
||||
if success { Ok(Scalar::from_u32(0)) } else { this.eval_libc("ERANGE") }
|
||||
}
|
||||
|
||||
fn sched_yield(&mut self) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
|
@ -418,13 +418,6 @@ fn emulate_foreign_item_by_name(
|
||||
// Indicate an error.
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
"GetFileInformationByHandleEx" if this.frame_in_std() => {
|
||||
#[allow(non_snake_case)]
|
||||
let [_hFile, _FileInformationClass, _lpFileInformation, _dwBufferSize] =
|
||||
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
|
||||
// Just make it fail.
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
"GetFileType" if this.frame_in_std() => {
|
||||
#[allow(non_snake_case)]
|
||||
let [_hFile] =
|
||||
|
@ -86,12 +86,12 @@ fn generate_diagnostic(&self) -> (String, SpanData) {
|
||||
impl fmt::Display for InvalidationCause {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
InvalidationCause::Access(kind) => write!(f, "{}", kind),
|
||||
InvalidationCause::Access(kind) => write!(f, "{kind}"),
|
||||
InvalidationCause::Retag(perm, kind) =>
|
||||
if *kind == RetagCause::FnEntry {
|
||||
write!(f, "{:?} FnEntry retag", perm)
|
||||
write!(f, "{perm:?} FnEntry retag")
|
||||
} else {
|
||||
write!(f, "{:?} retag", perm)
|
||||
write!(f, "{perm:?} retag")
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -339,7 +339,7 @@ pub fn get_logs_relevant_to(
|
||||
// this allocation.
|
||||
if self.history.base.0.tag() == tag {
|
||||
Some((
|
||||
format!("{:?} was created here, as the base tag for {:?}", tag, self.history.id),
|
||||
format!("{tag:?} was created here, as the base tag for {:?}", self.history.id),
|
||||
self.history.base.1.data()
|
||||
))
|
||||
} else {
|
||||
@ -381,7 +381,7 @@ pub fn grant_error(&self, perm: Permission, stack: &Stack) -> InterpError<'tcx>
|
||||
self.offset.bytes(),
|
||||
);
|
||||
err_sb_ub(
|
||||
format!("{}{}", action, error_cause(stack, op.orig_tag)),
|
||||
format!("{action}{}", error_cause(stack, op.orig_tag)),
|
||||
Some(operation_summary(&op.cause.summary(), self.history.id, op.range)),
|
||||
op.orig_tag.and_then(|orig_tag| self.get_logs_relevant_to(orig_tag, None)),
|
||||
)
|
||||
@ -401,7 +401,7 @@ pub fn access_error(&self, stack: &Stack) -> InterpError<'tcx> {
|
||||
offset = self.offset.bytes(),
|
||||
);
|
||||
err_sb_ub(
|
||||
format!("{}{}", action, error_cause(stack, op.tag)),
|
||||
format!("{action}{}", error_cause(stack, op.tag)),
|
||||
Some(operation_summary("an access", self.history.id, op.range)),
|
||||
op.tag.and_then(|tag| self.get_logs_relevant_to(tag, None)),
|
||||
)
|
||||
|
@ -1153,7 +1153,10 @@ fn print_stacks(&mut self, alloc_id: AllocId) -> InterpResult<'tcx> {
|
||||
let alloc_extra = this.get_alloc_extra(alloc_id)?;
|
||||
let stacks = alloc_extra.stacked_borrows.as_ref().unwrap().borrow();
|
||||
for (range, stack) in stacks.stacks.iter_all() {
|
||||
print!("{:?}: [", range);
|
||||
print!("{range:?}: [");
|
||||
if let Some(bottom) = stack.unknown_bottom() {
|
||||
print!(" unknown-bottom(..{bottom:?})");
|
||||
}
|
||||
for i in 0..stack.len() {
|
||||
let item = stack.get(i).unwrap();
|
||||
print!(" {:?}{:?}", item.perm(), item.tag());
|
||||
|
@ -1,10 +1,14 @@
|
||||
//@ignore-target-windows: No libc on Windows
|
||||
#![feature(cstr_from_bytes_until_nul)]
|
||||
use std::ffi::CStr;
|
||||
use std::thread;
|
||||
|
||||
fn main() {
|
||||
test_mutex_libc_init_recursive();
|
||||
test_mutex_libc_init_normal();
|
||||
test_mutex_libc_init_errorcheck();
|
||||
test_rwlock_libc_static_initializer();
|
||||
test_named_thread_truncation();
|
||||
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
test_mutex_libc_static_initializer_recursive();
|
||||
@ -125,3 +129,24 @@ fn test_rwlock_libc_static_initializer() {
|
||||
assert_eq!(libc::pthread_rwlock_destroy(rw.get()), 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_named_thread_truncation() {
|
||||
let long_name = std::iter::once("test_named_thread_truncation")
|
||||
.chain(std::iter::repeat(" yada").take(100))
|
||||
.collect::<String>();
|
||||
|
||||
let result = thread::Builder::new().name(long_name.clone()).spawn(move || {
|
||||
// Rust remembers the full thread name itself.
|
||||
assert_eq!(thread::current().name(), Some(long_name.as_str()));
|
||||
|
||||
// But the system is limited -- make sure we successfully set a truncation.
|
||||
let mut buf = vec![0u8; long_name.len() + 1];
|
||||
unsafe {
|
||||
libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len());
|
||||
}
|
||||
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
||||
assert!(cstr.to_bytes().len() >= 15); // POSIX seems to promise at least 15 chars
|
||||
assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));
|
||||
});
|
||||
result.unwrap().join().unwrap();
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//@compile-flags: -Zmiri-permissive-provenance
|
||||
#![feature(strict_provenance)]
|
||||
use std::{
|
||||
alloc::{self, Layout},
|
||||
mem::ManuallyDrop,
|
||||
@ -5,25 +7,40 @@
|
||||
|
||||
extern "Rust" {
|
||||
fn miri_get_alloc_id(ptr: *const u8) -> u64;
|
||||
fn miri_print_stacks(alloc_id: u64);
|
||||
fn miri_print_borrow_stacks(alloc_id: u64);
|
||||
}
|
||||
|
||||
fn get_alloc_id(ptr: *const u8) -> u64 {
|
||||
unsafe { miri_get_alloc_id(ptr) }
|
||||
}
|
||||
|
||||
fn print_borrow_stacks(alloc_id: u64) {
|
||||
unsafe { miri_print_borrow_stacks(alloc_id) }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let ptr = unsafe { alloc::alloc(Layout::new::<u8>()) };
|
||||
let alloc_id = unsafe { miri_get_alloc_id(ptr) };
|
||||
unsafe { miri_print_stacks(alloc_id) };
|
||||
let alloc_id = get_alloc_id(ptr);
|
||||
print_borrow_stacks(alloc_id);
|
||||
|
||||
assert!(!ptr.is_null());
|
||||
unsafe { miri_print_stacks(alloc_id) };
|
||||
print_borrow_stacks(alloc_id);
|
||||
|
||||
unsafe { *ptr = 42 };
|
||||
unsafe { miri_print_stacks(alloc_id) };
|
||||
print_borrow_stacks(alloc_id);
|
||||
|
||||
let _b = unsafe { ManuallyDrop::new(Box::from_raw(ptr)) };
|
||||
unsafe { miri_print_stacks(alloc_id) };
|
||||
print_borrow_stacks(alloc_id);
|
||||
|
||||
let _ptr = unsafe { &*ptr };
|
||||
unsafe { miri_print_stacks(alloc_id) };
|
||||
print_borrow_stacks(alloc_id);
|
||||
|
||||
// Create an unknown bottom, and print it
|
||||
let ptr = ptr as usize as *mut u8;
|
||||
unsafe {
|
||||
*ptr = 5;
|
||||
}
|
||||
print_borrow_stacks(alloc_id);
|
||||
|
||||
unsafe { alloc::dealloc(ptr, Layout::new::<u8>()) };
|
||||
}
|
||||
|
@ -3,3 +3,4 @@
|
||||
0..1: [ SharedReadWrite<TAG> ]
|
||||
0..1: [ SharedReadWrite<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> ]
|
||||
0..1: [ SharedReadWrite<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> SharedReadOnly<TAG> ]
|
||||
0..1: [ unknown-bottom(..<TAG>) ]
|
||||
|
Loading…
Reference in New Issue
Block a user