Auto merge of #140436 - RalfJung:miri-sync, r=RalfJung

Miri subtree update

r? `@ghost`
This commit is contained in:
bors 2025-04-29 08:44:25 +00:00
commit efcbb94dcc
47 changed files with 802 additions and 307 deletions

View file

@ -89,14 +89,52 @@ jobs:
# Check if all jobs that we depend on (in the needs array) were successful.
jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
cron-fail-notify:
name: cronjob failure notification
cron-rustc-pull:
name: automatic pull from rustc
runs-on: ubuntu-latest
permissions:
# The cronjob needs to be able to push to the repo...
contents: write
# ... and create a PR.
pull-requests: write
if: ${{ github.event_name == 'schedule' }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 256 # get a bit more of the history
- name: install josh-proxy
run: cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04
- name: setup bot git name and email
run: |
git config --global user.name 'The Miri Cronjob Bot'
git config --global user.email 'miri@cron.bot'
- name: Install nightly toolchain
run: rustup toolchain install nightly --profile minimal
- name: get changes from rustc
run: ./miri rustc-pull
- name: Install rustup-toolchain-install-master
run: cargo install -f rustup-toolchain-install-master
- name: format changes (if any)
run: |
./miri toolchain
./miri fmt --check || (./miri fmt && git commit -am "fmt")
- name: Push changes to a branch and create PR
run: |
# `git diff --exit-code` "succeeds" if the diff is empty.
if git diff --exit-code HEAD^; then echo "Nothing changed in rustc, skipping PR"; exit 0; fi
# The diff is non-empty, create a PR.
BRANCH="rustup-$(date -u +%Y-%m-%d)"
git switch -c $BRANCH
git push -u origin $BRANCH
gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }}
ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }}
cron-fail-notify:
name: cronjob failure notification
runs-on: ubuntu-latest
needs: [build, style, coverage]
if: ${{ github.event_name == 'schedule' && failure() }}
steps:
@ -122,39 +160,3 @@ jobs:
Thanks in advance!
Sincerely,
The Miri Cronjobs Bot'
# Attempt to auto-sync with rustc
- uses: actions/checkout@v4
with:
fetch-depth: 256 # get a bit more of the history
- name: install josh-proxy
run: cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04
- name: setup bot git name and email
run: |
git config --global user.name 'The Miri Cronjob Bot'
git config --global user.email 'miri@cron.bot'
- name: Install nightly toolchain
run: rustup toolchain install nightly --profile minimal
- name: get changes from rustc
run: ./miri rustc-pull
- name: Install rustup-toolchain-install-master
run: cargo install -f rustup-toolchain-install-master
- name: format changes (if any)
run: |
./miri toolchain
./miri fmt --check || (./miri fmt && git commit -am "fmt")
- name: Push changes to a branch
run: |
BRANCH="rustup-$(date -u +%Y-%m-%d)"
git switch -c $BRANCH
git push -u origin $BRANCH
- name: Create Pull Request
run: |
PR=$(gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.')
~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \
--stream miri --subject "Miri Build Failure ($(date -u +%Y-%m))" \
--message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience."
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }}
ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }}

View file

@ -489,7 +489,7 @@ Miri knows where it is supposed to start execution:
```rust
#[cfg(miri)]
#[no_mangle]
#[unsafe(no_mangle)]
fn miri_start(argc: isize, argv: *const *const u8) -> isize {
// Call the actual start function that your project implements, based on your target's conventions.
}

View file

@ -13,7 +13,7 @@ fn read_i16(buffer: &[u8], index: usize) -> i16 {
const SIZE: usize = size_of::<i16>();
let mut bytes: [u8; SIZE] = [0u8; SIZE];
bytes.copy_from_slice(&buffer[(index * SIZE)..(index * SIZE + SIZE)]);
unsafe { std::mem::transmute(bytes) }
i16::from_ne_bytes(bytes)
}
fn mse(samples: usize, frame_buf: &[i16], buf_ref: &[u8]) -> f64 {

View file

@ -1 +1 @@
1bc56185ee257ed829a0aea7abdc3b03c5fed887
1b8ab72680f36e783af84c1a3c4f8508572bd9f9

View file

@ -107,47 +107,6 @@ fn align_addr(addr: u64, align: u64) -> u64 {
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Returns the exposed `AllocId` that corresponds to the specified addr,
// or `None` if the addr is out of bounds
fn alloc_id_from_addr(&self, addr: u64, size: i64) -> Option<AllocId> {
let this = self.eval_context_ref();
let global_state = this.machine.alloc_addresses.borrow();
assert!(global_state.provenance_mode != ProvenanceMode::Strict);
// We always search the allocation to the right of this address. So if the size is structly
// negative, we have to search for `addr-1` instead.
let addr = if size >= 0 { addr } else { addr.saturating_sub(1) };
let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr);
// Determine the in-bounds provenance for this pointer.
let alloc_id = match pos {
Ok(pos) => Some(global_state.int_to_ptr_map[pos].1),
Err(0) => None,
Err(pos) => {
// This is the largest of the addresses smaller than `int`,
// i.e. the greatest lower bound (glb)
let (glb, alloc_id) = global_state.int_to_ptr_map[pos - 1];
// This never overflows because `addr >= glb`
let offset = addr - glb;
// We require this to be strict in-bounds of the allocation. This arm is only
// entered for addresses that are not the base address, so even zero-sized
// allocations will get recognized at their base address -- but all other
// allocations will *not* be recognized at their "end" address.
let size = this.get_alloc_info(alloc_id).size;
if offset < size.bytes() { Some(alloc_id) } else { None }
}
}?;
// We only use this provenance if it has been exposed.
if global_state.exposed.contains(&alloc_id) {
// This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed.
debug_assert!(this.is_alloc_live(alloc_id));
Some(alloc_id)
} else {
None
}
}
fn addr_from_alloc_id_uncached(
&self,
global_state: &mut GlobalStateInner,
@ -242,11 +201,65 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
interp_ok(base_addr)
}
}
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Returns the `AllocId` that corresponds to the specified addr,
// or `None` if the addr is out of bounds.
// Setting `only_exposed_allocations` selects whether only exposed allocations are considered.
fn alloc_id_from_addr(
&self,
addr: u64,
size: i64,
only_exposed_allocations: bool,
) -> Option<AllocId> {
let this = self.eval_context_ref();
let global_state = this.machine.alloc_addresses.borrow();
assert!(global_state.provenance_mode != ProvenanceMode::Strict);
// We always search the allocation to the right of this address. So if the size is strictly
// negative, we have to search for `addr-1` instead.
let addr = if size >= 0 { addr } else { addr.saturating_sub(1) };
let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr);
// Determine the in-bounds provenance for this pointer.
let alloc_id = match pos {
Ok(pos) => Some(global_state.int_to_ptr_map[pos].1),
Err(0) => None,
Err(pos) => {
// This is the largest of the addresses smaller than `int`,
// i.e. the greatest lower bound (glb)
let (glb, alloc_id) = global_state.int_to_ptr_map[pos - 1];
// This never overflows because `addr >= glb`
let offset = addr - glb;
// We require this to be strict in-bounds of the allocation. This arm is only
// entered for addresses that are not the base address, so even zero-sized
// allocations will get recognized at their base address -- but all other
// allocations will *not* be recognized at their "end" address.
let size = this.get_alloc_info(alloc_id).size;
if offset < size.bytes() { Some(alloc_id) } else { None }
}
}?;
// We only use this provenance if it has been exposed, or if the caller requested also non-exposed allocations
if !only_exposed_allocations || global_state.exposed.contains(&alloc_id) {
// This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed.
debug_assert!(this.is_alloc_live(alloc_id));
Some(alloc_id)
} else {
None
}
}
/// Returns the base address of an allocation, or an error if no base address could be found
///
/// # Panics
/// If `memory_kind = None` and the `alloc_id` is not cached, meaning that the first call to this function per `alloc_id` must get the `memory_kind`.
fn addr_from_alloc_id(
&self,
alloc_id: AllocId,
memory_kind: MemoryKind,
memory_kind: Option<MemoryKind>,
) -> InterpResult<'tcx, u64> {
let this = self.eval_context_ref();
let mut global_state = this.machine.alloc_addresses.borrow_mut();
@ -256,8 +269,10 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
Some(&addr) => interp_ok(addr),
None => {
// First time we're looking for the absolute address of this allocation.
let memory_kind =
memory_kind.expect("memory_kind is required since alloc_id is not cached");
let base_addr =
self.addr_from_alloc_id_uncached(global_state, alloc_id, memory_kind)?;
this.addr_from_alloc_id_uncached(global_state, alloc_id, memory_kind)?;
trace!("Assigning base address {:#x} to allocation {:?}", base_addr, alloc_id);
// Store address in cache.
@ -283,10 +298,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
}
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn expose_provenance(&self, provenance: Provenance) -> InterpResult<'tcx> {
let this = self.eval_context_ref();
let mut global_state = this.machine.alloc_addresses.borrow_mut();
@ -365,7 +377,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let alloc_id = prov.alloc_id();
// Get a pointer to the beginning of this allocation.
let base_addr = this.addr_from_alloc_id(alloc_id, kind)?;
let base_addr = this.addr_from_alloc_id(alloc_id, Some(kind))?;
let base_ptr = interpret::Pointer::new(
Provenance::Concrete { alloc_id, tag },
Size::from_bytes(base_addr),
@ -388,7 +400,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// In native lib mode, MiriAllocBytes for global allocations are handled via `prepared_alloc_bytes`.
// This additional call ensures that some `MiriAllocBytes` are always prepared, just in case
// this function gets called before the first time `addr_from_alloc_id` gets called.
this.addr_from_alloc_id(id, MiriMemoryKind::Global.into())?;
this.addr_from_alloc_id(id, Some(MiriMemoryKind::Global.into()))?;
// The memory we need here will have already been allocated during an earlier call to
// `addr_from_alloc_id` for this allocation. So don't create a new `MiriAllocBytes` here, instead
// fetch the previously prepared bytes from `prepared_alloc_bytes`.
@ -423,7 +435,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
alloc_id
} else {
// A wildcard pointer.
this.alloc_id_from_addr(addr.bytes(), size)?
let only_exposed_allocations = true;
this.alloc_id_from_addr(addr.bytes(), size, only_exposed_allocations)?
};
// This cannot fail: since we already have a pointer with that provenance, adjust_alloc_root_pointer

View file

@ -106,7 +106,7 @@ fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, MiriEntryFnType) {
} else {
tcx.dcx().fatal(
"`miri_start` must have the following signature:\n\
fn miri_start(argc: isize, argv: *const *const u8) -> isize",
fn miri_start(argc: isize, argv: *const *const u8) -> isize",
);
}
} else {
@ -115,7 +115,7 @@ fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, MiriEntryFnType) {
Alternatively, you can export a `miri_start` function:\n\
\n\
#[cfg(miri)]\n\
#[no_mangle]\n\
#[unsafe(no_mangle)]\n\
fn miri_start(argc: isize, argv: *const *const u8) -> isize {\
\n // Call the actual start function that your project implements, based on your target's conventions.\n\
}"

View file

@ -61,8 +61,7 @@ fn all_read_accesses_commute() {
// ... and produce the same final result.
assert_eq!(
loc12, loc21,
"Read accesses {:?} followed by {:?} do not commute !",
rel1, rel2
"Read accesses {rel1:?} followed by {rel2:?} do not commute !"
);
}
}
@ -674,8 +673,8 @@ mod spurious_read {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (x, y) = self.retag_permissions();
write!(f, "{}; ", self.xy_rel)?;
write!(f, "y: ({}); ", y,)?;
write!(f, "retag x ({}); ", x)?;
write!(f, "y: ({y}); ")?;
write!(f, "retag x ({x}); ")?;
write!(f, "<arbitrary code>; <spurious read x>;")?;
Ok(())
@ -730,17 +729,17 @@ mod spurious_read {
// protector.
final_source
.distinguishable::</*X*/ AllowRet, /*Y*/ AllowRet>(&final_target)
.then_some(format!("{}", final_target))
.then_some(format!("{final_target}"))
} else {
Some(format!("UB"))
}
};
if let Some(final_target) = distinguishable {
eprintln!(
"For pattern '{}', inserting a spurious read through x makes the final state '{}' instead of '{}' which is observable",
pat, final_target, final_source
"For pattern '{pat}', inserting a spurious read through x makes the final state '{final_target}' \
instead of '{final_source}' which is observable"
);
eprintln!(" (arbitrary code instanciated with '{}')", opaque);
eprintln!(" (arbitrary code instanciated with '{opaque}')");
err += 1;
// We found an instanciation of the opaque code that makes this Pattern
// fail, we don't really need to check the rest.

View file

@ -381,7 +381,7 @@ impl AccessType {
});
if let Some(ty) = ty {
msg.push_str(&format!(" of type `{}`", ty));
msg.push_str(&format!(" of type `{ty}`"));
}
msg

View file

@ -1017,7 +1017,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// Check that the given `caller_fn_abi` matches the expected ABI described by
/// `callee_abi`, `callee_input_tys`, `callee_output_ty`, and the return the list of
/// `callee_abi`, `callee_input_tys`, `callee_output_ty`, and then returns the list of
/// arguments.
fn check_shim_abi<'a, const N: usize>(
&mut self,

View file

@ -721,9 +721,8 @@ impl<'tcx> MiriMachine<'tcx> {
// Check if host target == the session target.
if host_triple != target_triple {
panic!(
"calling external C functions in linked .so file requires host and target to be the same: host={}, target={}",
host_triple,
target_triple,
"calling native C functions in linked .so file requires host and target to be the same: \
host={host_triple}, target={target_triple}",
);
}
// Note: it is the user's responsibility to provide a correct SO file.

View file

@ -21,7 +21,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
clk_id_op: &OpTy<'tcx>,
tp_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
// This clock support is deliberately minimal because a lot of clock types have fiddly
// properties (is it possible for Miri to be suspended independently of the host?). If you
// have a use for another clock type, please open an issue.
@ -29,8 +30,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
this.assert_target_os_is_unix("clock_gettime");
let clockid_t_size = this.libc_ty_layout("clockid_t").size;
let clk_id = this.read_scalar(clk_id_op)?.to_i32()?;
let clk_id = this.read_scalar(clk_id_op)?.to_int(clockid_t_size)?;
let tp = this.deref_pointer_as(tp_op, this.libc_ty_layout("timespec"))?;
let absolute_clocks;
@ -43,34 +45,34 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Linux further distinguishes regular and "coarse" clocks, but the "coarse" version
// is just specified to be "faster and less precise", so we implement both the same way.
absolute_clocks = vec![
this.eval_libc_i32("CLOCK_REALTIME"),
this.eval_libc_i32("CLOCK_REALTIME_COARSE"),
this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?,
this.eval_libc("CLOCK_REALTIME_COARSE").to_int(clockid_t_size)?,
];
// The second kind is MONOTONIC clocks for which 0 is an arbitrary time point, but they are
// never allowed to go backwards. We don't need to do any additional monotonicity
// enforcement because std::time::Instant already guarantees that it is monotonic.
relative_clocks = vec![
this.eval_libc_i32("CLOCK_MONOTONIC"),
this.eval_libc_i32("CLOCK_MONOTONIC_COARSE"),
this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?,
this.eval_libc("CLOCK_MONOTONIC_COARSE").to_int(clockid_t_size)?,
];
}
"macos" => {
absolute_clocks = vec![this.eval_libc_i32("CLOCK_REALTIME")];
relative_clocks = vec![this.eval_libc_i32("CLOCK_MONOTONIC")];
absolute_clocks = vec![this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?];
relative_clocks = vec![this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?];
// `CLOCK_UPTIME_RAW` supposed to not increment while the system is asleep... but
// that's not really something a program running inside Miri can tell, anyway.
// We need to support it because std uses it.
relative_clocks.push(this.eval_libc_i32("CLOCK_UPTIME_RAW"));
relative_clocks.push(this.eval_libc("CLOCK_UPTIME_RAW").to_int(clockid_t_size)?);
}
"solaris" | "illumos" => {
// The REALTIME clock returns the actual time since the Unix epoch.
absolute_clocks = vec![this.eval_libc_i32("CLOCK_REALTIME")];
absolute_clocks = vec![this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?];
// MONOTONIC, in the other hand, is the high resolution, non-adjustable
// clock from an arbitrary time in the past.
// Note that the man page mentions HIGHRES but it is just
// an alias of MONOTONIC and the libc crate does not expose it anyway.
// https://docs.oracle.com/cd/E23824_01/html/821-1465/clock-gettime-3c.html
relative_clocks = vec![this.eval_libc_i32("CLOCK_MONOTONIC")];
relative_clocks = vec![this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?];
}
target => throw_unsup_format!("`clock_gettime` is not supported on target OS {target}"),
}
@ -81,15 +83,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
} else if relative_clocks.contains(&clk_id) {
this.machine.monotonic_clock.now().duration_since(this.machine.monotonic_clock.epoch())
} else {
return this.set_last_error_and_return_i32(LibcError("EINVAL"));
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
};
let tv_sec = duration.as_secs();
let tv_nsec = duration.subsec_nanos();
this.write_int_fields(&[tv_sec.into(), tv_nsec.into()], &tp)?;
this.write_int(0, dest)?;
interp_ok(Scalar::from_i32(0))
interp_ok(())
}
fn gettimeofday(
@ -188,10 +191,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
tm_zone.push('+');
}
let offset_hour = offset_in_seconds.abs() / 3600;
write!(tm_zone, "{:02}", offset_hour).unwrap();
write!(tm_zone, "{offset_hour:02}").unwrap();
let offset_min = (offset_in_seconds.abs() % 3600) / 60;
if offset_min != 0 {
write!(tm_zone, "{:02}", offset_min).unwrap();
write!(tm_zone, "{offset_min:02}").unwrap();
}
// Add null terminator for C string compatibility.

View file

@ -112,51 +112,122 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// Environment related shims
"getenv" => {
let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
let [name] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.const_raw_ptr.ty],
this.machine.layouts.mut_raw_ptr.ty,
args,
)?;
let result = this.getenv(name)?;
this.write_pointer(result, dest)?;
}
"unsetenv" => {
let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
let [name] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.const_raw_ptr.ty],
this.tcx.types.i32,
args,
)?;
let result = this.unsetenv(name)?;
this.write_scalar(result, dest)?;
}
"setenv" => {
let [name, value, overwrite] = this.check_shim(abi, Conv::C, link_name, args)?;
let [name, value, overwrite] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[
this.machine.layouts.const_raw_ptr.ty,
this.machine.layouts.const_raw_ptr.ty,
this.tcx.types.i32,
],
this.tcx.types.i32,
args,
)?;
this.read_scalar(overwrite)?.to_i32()?;
let result = this.setenv(name, value)?;
this.write_scalar(result, dest)?;
}
"getcwd" => {
let [buf, size] = this.check_shim(abi, Conv::C, link_name, args)?;
let [buf, size] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.mut_raw_ptr.ty, this.tcx.types.usize],
this.machine.layouts.mut_raw_ptr.ty,
args,
)?;
let result = this.getcwd(buf, size)?;
this.write_pointer(result, dest)?;
}
"chdir" => {
let [path] = this.check_shim(abi, Conv::C, link_name, args)?;
let [path] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.const_raw_ptr.ty],
this.tcx.types.i32,
args,
)?;
let result = this.chdir(path)?;
this.write_scalar(result, dest)?;
}
"getpid" => {
let [] = this.check_shim(abi, Conv::C, link_name, args)?;
let [] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[],
this.libc_ty_layout("pid_t").ty,
args,
)?;
let result = this.getpid()?;
this.write_scalar(result, dest)?;
}
"sysconf" => {
let [val] = this.check_shim(abi, Conv::C, link_name, args)?;
let [val] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.tcx.types.i32],
this.tcx.types.isize,
args,
)?;
let result = this.sysconf(val)?;
this.write_scalar(result, dest)?;
}
// File descriptors
"read" => {
let [fd, buf, count] = this.check_shim(abi, Conv::C, link_name, args)?;
let [fd, buf, count] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.tcx.types.i32, this.machine.layouts.mut_raw_ptr.ty, this.tcx.types.usize],
this.tcx.types.isize,
args,
)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
this.read(fd, buf, count, None, dest)?;
}
"write" => {
let [fd, buf, n] = this.check_shim(abi, Conv::C, link_name, args)?;
let [fd, buf, n] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[
this.tcx.types.i32,
this.machine.layouts.const_raw_ptr.ty,
this.tcx.types.usize,
],
this.tcx.types.isize,
args,
)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
@ -164,38 +235,88 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write(fd, buf, count, None, dest)?;
}
"pread" => {
let [fd, buf, count, offset] = this.check_shim(abi, Conv::C, link_name, args)?;
let off_t = this.libc_ty_layout("off_t");
let [fd, buf, count, offset] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[
this.tcx.types.i32,
this.machine.layouts.mut_raw_ptr.ty,
this.tcx.types.usize,
off_t.ty,
],
this.tcx.types.isize,
args,
)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
let offset = this.read_scalar(offset)?.to_int(off_t.size)?;
this.read(fd, buf, count, Some(offset), dest)?;
}
"pwrite" => {
let [fd, buf, n, offset] = this.check_shim(abi, Conv::C, link_name, args)?;
let off_t = this.libc_ty_layout("off_t");
let [fd, buf, n, offset] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[
this.tcx.types.i32,
this.machine.layouts.const_raw_ptr.ty,
this.tcx.types.usize,
off_t.ty,
],
this.tcx.types.isize,
args,
)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
let offset = this.read_scalar(offset)?.to_int(off_t.size)?;
trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
this.write(fd, buf, count, Some(offset), dest)?;
}
"pread64" => {
let [fd, buf, count, offset] = this.check_shim(abi, Conv::C, link_name, args)?;
let off64_t = this.libc_ty_layout("off64_t");
let [fd, buf, count, offset] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[
this.tcx.types.i32,
this.machine.layouts.mut_raw_ptr.ty,
this.tcx.types.usize,
off64_t.ty,
],
this.tcx.types.isize,
args,
)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
let offset =
this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
let offset = this.read_scalar(offset)?.to_int(off64_t.size)?;
this.read(fd, buf, count, Some(offset), dest)?;
}
"pwrite64" => {
let [fd, buf, n, offset] = this.check_shim(abi, Conv::C, link_name, args)?;
let off64_t = this.libc_ty_layout("off64_t");
let [fd, buf, n, offset] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[
this.tcx.types.i32,
this.machine.layouts.const_raw_ptr.ty,
this.tcx.types.usize,
off64_t.ty,
],
this.tcx.types.isize,
args,
)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
let offset =
this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
let offset = this.read_scalar(offset)?.to_int(off64_t.size)?;
trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
this.write(fd, buf, count, Some(offset), dest)?;
}
@ -218,13 +339,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"dup" => {
let [old_fd] = this.check_shim(abi, Conv::C, link_name, args)?;
let [old_fd] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.tcx.types.i32],
this.tcx.types.i32,
args,
)?;
let old_fd = this.read_scalar(old_fd)?.to_i32()?;
let new_fd = this.dup(old_fd)?;
this.write_scalar(new_fd, dest)?;
}
"dup2" => {
let [old_fd, new_fd] = this.check_shim(abi, Conv::C, link_name, args)?;
let [old_fd, new_fd] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.tcx.types.i32, this.tcx.types.i32],
this.tcx.types.i32,
args,
)?;
let old_fd = this.read_scalar(old_fd)?.to_i32()?;
let new_fd = this.read_scalar(new_fd)?.to_i32()?;
let result = this.dup2(old_fd, new_fd)?;
@ -233,7 +368,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"flock" => {
// Currently this function does not exist on all Unixes, e.g. on Solaris.
this.check_target_os(&["linux", "freebsd", "macos", "illumos"], link_name)?;
let [fd, op] = this.check_shim(abi, Conv::C, link_name, args)?;
let [fd, op] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.tcx.types.i32, this.tcx.types.i32],
this.tcx.types.i32,
args,
)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let op = this.read_scalar(op)?.to_i32()?;
let result = this.flock(fd, op)?;
@ -250,140 +392,311 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"unlink" => {
let [path] = this.check_shim(abi, Conv::C, link_name, args)?;
let [path] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.const_raw_ptr.ty],
this.tcx.types.i32,
args,
)?;
let result = this.unlink(path)?;
this.write_scalar(result, dest)?;
}
"symlink" => {
let [target, linkpath] = this.check_shim(abi, Conv::C, link_name, args)?;
let [target, linkpath] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.const_raw_ptr.ty],
this.tcx.types.i32,
args,
)?;
let result = this.symlink(target, linkpath)?;
this.write_scalar(result, dest)?;
}
"rename" => {
let [oldpath, newpath] = this.check_shim(abi, Conv::C, link_name, args)?;
let [oldpath, newpath] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.const_raw_ptr.ty],
this.tcx.types.i32,
args,
)?;
let result = this.rename(oldpath, newpath)?;
this.write_scalar(result, dest)?;
}
"mkdir" => {
let [path, mode] = this.check_shim(abi, Conv::C, link_name, args)?;
let [path, mode] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.const_raw_ptr.ty, this.libc_ty_layout("mode_t").ty],
this.tcx.types.i32,
args,
)?;
let result = this.mkdir(path, mode)?;
this.write_scalar(result, dest)?;
}
"rmdir" => {
let [path] = this.check_shim(abi, Conv::C, link_name, args)?;
let [path] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.const_raw_ptr.ty],
this.tcx.types.i32,
args,
)?;
let result = this.rmdir(path)?;
this.write_scalar(result, dest)?;
}
"opendir" => {
let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
let [name] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.const_raw_ptr.ty],
this.machine.layouts.mut_raw_ptr.ty,
args,
)?;
let result = this.opendir(name)?;
this.write_scalar(result, dest)?;
}
"closedir" => {
let [dirp] = this.check_shim(abi, Conv::C, link_name, args)?;
let [dirp] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.mut_raw_ptr.ty],
this.tcx.types.i32,
args,
)?;
let result = this.closedir(dirp)?;
this.write_scalar(result, dest)?;
}
"lseek64" => {
let [fd, offset, whence] = this.check_shim(abi, Conv::C, link_name, args)?;
let off64_t = this.libc_ty_layout("off64_t");
let [fd, offset, whence] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.tcx.types.i32, off64_t.ty, this.tcx.types.i32],
off64_t.ty,
args,
)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let offset = this.read_scalar(offset)?.to_i64()?;
let offset = this.read_scalar(offset)?.to_int(off64_t.size)?;
let whence = this.read_scalar(whence)?.to_i32()?;
let result = this.lseek64(fd, offset.into(), whence)?;
this.write_scalar(result, dest)?;
this.lseek64(fd, offset, whence, dest)?;
}
"lseek" => {
let [fd, offset, whence] = this.check_shim(abi, Conv::C, link_name, args)?;
let off_t = this.libc_ty_layout("off_t");
let [fd, offset, whence] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.tcx.types.i32, off_t.ty, this.tcx.types.i32],
off_t.ty,
args,
)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
let offset = this.read_scalar(offset)?.to_int(off_t.size)?;
let whence = this.read_scalar(whence)?.to_i32()?;
let result = this.lseek64(fd, offset, whence)?;
this.write_scalar(result, dest)?;
this.lseek64(fd, offset, whence, dest)?;
}
"ftruncate64" => {
let [fd, length] = this.check_shim(abi, Conv::C, link_name, args)?;
let off64_t = this.libc_ty_layout("off64_t");
let [fd, length] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.tcx.types.i32, off64_t.ty],
this.tcx.types.i32,
args,
)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let length = this.read_scalar(length)?.to_i64()?;
let result = this.ftruncate64(fd, length.into())?;
let length = this.read_scalar(length)?.to_int(off64_t.size)?;
let result = this.ftruncate64(fd, length)?;
this.write_scalar(result, dest)?;
}
"ftruncate" => {
let [fd, length] = this.check_shim(abi, Conv::C, link_name, args)?;
let off_t = this.libc_ty_layout("off_t");
let [fd, length] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.tcx.types.i32, off_t.ty],
this.tcx.types.i32,
args,
)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let length = this.read_scalar(length)?.to_int(this.libc_ty_layout("off_t").size)?;
let length = this.read_scalar(length)?.to_int(off_t.size)?;
let result = this.ftruncate64(fd, length)?;
this.write_scalar(result, dest)?;
}
"fsync" => {
let [fd] = this.check_shim(abi, Conv::C, link_name, args)?;
let [fd] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.tcx.types.i32],
this.tcx.types.i32,
args,
)?;
let result = this.fsync(fd)?;
this.write_scalar(result, dest)?;
}
"fdatasync" => {
let [fd] = this.check_shim(abi, Conv::C, link_name, args)?;
let [fd] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.tcx.types.i32],
this.tcx.types.i32,
args,
)?;
let result = this.fdatasync(fd)?;
this.write_scalar(result, dest)?;
}
"readlink" => {
let [pathname, buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?;
let [pathname, buf, bufsize] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[
this.machine.layouts.const_raw_ptr.ty,
this.machine.layouts.mut_raw_ptr.ty,
this.tcx.types.usize,
],
this.tcx.types.isize,
args,
)?;
let result = this.readlink(pathname, buf, bufsize)?;
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
"posix_fadvise" => {
let [fd, offset, len, advice] = this.check_shim(abi, Conv::C, link_name, args)?;
let off_t = this.libc_ty_layout("off_t");
let [fd, offset, len, advice] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.tcx.types.i32, off_t.ty, off_t.ty, this.tcx.types.i32],
this.tcx.types.i32,
args,
)?;
this.read_scalar(fd)?.to_i32()?;
this.read_target_isize(offset)?;
this.read_target_isize(len)?;
this.read_scalar(offset)?.to_int(off_t.size)?;
this.read_scalar(len)?.to_int(off_t.size)?;
this.read_scalar(advice)?.to_i32()?;
// fadvise is only informational, we can ignore it.
this.write_null(dest)?;
}
"realpath" => {
let [path, resolved_path] = this.check_shim(abi, Conv::C, link_name, args)?;
let [path, resolved_path] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.mut_raw_ptr.ty],
this.machine.layouts.mut_raw_ptr.ty,
args,
)?;
let result = this.realpath(path, resolved_path)?;
this.write_scalar(result, dest)?;
}
"mkstemp" => {
let [template] = this.check_shim(abi, Conv::C, link_name, args)?;
let [template] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.mut_raw_ptr.ty],
this.tcx.types.i32,
args,
)?;
let result = this.mkstemp(template)?;
this.write_scalar(result, dest)?;
}
// Unnamed sockets and pipes
"socketpair" => {
let [domain, type_, protocol, sv] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [domain, type_, protocol, sv] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[
this.tcx.types.i32,
this.tcx.types.i32,
this.tcx.types.i32,
this.machine.layouts.mut_raw_ptr.ty,
],
this.tcx.types.i32,
args,
)?;
let result = this.socketpair(domain, type_, protocol, sv)?;
this.write_scalar(result, dest)?;
}
"pipe" => {
let [pipefd] = this.check_shim(abi, Conv::C, link_name, args)?;
let [pipefd] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.mut_raw_ptr.ty],
this.tcx.types.i32,
args,
)?;
let result = this.pipe2(pipefd, /*flags*/ None)?;
this.write_scalar(result, dest)?;
}
"pipe2" => {
// Currently this function does not exist on all Unixes, e.g. on macOS.
this.check_target_os(&["linux", "freebsd", "solaris", "illumos"], link_name)?;
let [pipefd, flags] = this.check_shim(abi, Conv::C, link_name, args)?;
let [pipefd, flags] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.mut_raw_ptr.ty, this.tcx.types.i32],
this.tcx.types.i32,
args,
)?;
let result = this.pipe2(pipefd, Some(flags))?;
this.write_scalar(result, dest)?;
}
// Time
"gettimeofday" => {
let [tv, tz] = this.check_shim(abi, Conv::C, link_name, args)?;
let [tv, tz] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.mut_raw_ptr.ty, this.machine.layouts.mut_raw_ptr.ty],
this.tcx.types.i32,
args,
)?;
let result = this.gettimeofday(tv, tz)?;
this.write_scalar(result, dest)?;
}
"localtime_r" => {
let [timep, result_op] = this.check_shim(abi, Conv::C, link_name, args)?;
let [timep, result_op] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.mut_raw_ptr.ty],
this.machine.layouts.mut_raw_ptr.ty,
args,
)?;
let result = this.localtime_r(timep, result_op)?;
this.write_pointer(result, dest)?;
}
"clock_gettime" => {
let [clk_id, tp] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.clock_gettime(clk_id, tp)?;
this.write_scalar(result, dest)?;
let [clk_id, tp] = this.check_shim_abi(
link_name,
abi,
ExternAbi::C { unwind: false },
[this.libc_ty_layout("clockid_t").ty, this.machine.layouts.mut_raw_ptr.ty],
this.tcx.types.i32,
args,
)?;
this.clock_gettime(clk_id, tp, dest)?;
}
// Allocation
@ -834,7 +1147,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// These shims are enabled only when the caller is in the standard library.
"pthread_attr_getguardsize" if this.frame_in_std() => {
let [_attr, guard_size] = this.check_shim(abi, Conv::C, link_name, args)?;
let guard_size_layout = this.libc_ty_layout("size_t");
let guard_size_layout = this.machine.layouts.usize;
let guard_size = this.deref_pointer_as(guard_size, guard_size_layout)?;
this.write_scalar(
Scalar::from_uint(this.machine.page_size, guard_size_layout.size),

View file

@ -502,7 +502,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
interp_ok(Scalar::from_i32(this.try_unwrap_io_result(fd)?))
}
fn lseek64(&mut self, fd_num: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> {
fn lseek64(
&mut self,
fd_num: i32,
offset: i128,
whence: i32,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
// Isolation check is done via `FileDescription` trait.
@ -510,7 +516,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let seek_from = if whence == this.eval_libc_i32("SEEK_SET") {
if offset < 0 {
// Negative offsets return `EINVAL`.
return this.set_last_error_and_return_i64(LibcError("EINVAL"));
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
} else {
SeekFrom::Start(u64::try_from(offset).unwrap())
}
@ -519,19 +525,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
} else if whence == this.eval_libc_i32("SEEK_END") {
SeekFrom::End(i64::try_from(offset).unwrap())
} else {
return this.set_last_error_and_return_i64(LibcError("EINVAL"));
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
};
let communicate = this.machine.communicate();
let Some(fd) = this.machine.fds.get(fd_num) else {
return this.set_last_error_and_return_i64(LibcError("EBADF"));
return this.set_last_error_and_return(LibcError("EBADF"), dest);
};
let result = fd.seek(communicate, seek_from)?.map(|offset| i64::try_from(offset).unwrap());
drop(fd);
let result = this.try_unwrap_io_result(result)?;
interp_ok(Scalar::from_i64(result))
this.write_int(result, dest)?;
interp_ok(())
}
fn unlink(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {

View file

@ -317,6 +317,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let res = this.GetFileInformationByHandle(handle, info)?;
this.write_scalar(res, dest)?;
}
"DeleteFileW" => {
let [file_name] = this.check_shim(abi, sys_conv, link_name, args)?;
let res = this.DeleteFileW(file_name)?;
this.write_scalar(res, dest)?;
}
// Allocation
"HeapAlloc" => {

View file

@ -371,6 +371,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
interp_ok(this.eval_windows("c", "TRUE"))
}
fn DeleteFileW(
&mut self,
file_name: &OpTy<'tcx>, // LPCWSTR
) -> InterpResult<'tcx, Scalar> {
// ^ Returns BOOL (i32 on Windows)
let this = self.eval_context_mut();
this.assert_target_os("windows", "DeleteFileW");
this.check_no_isolation("`DeleteFileW`")?;
let file_name = this.read_path_from_wide_str(this.read_pointer(file_name)?)?;
match std::fs::remove_file(file_name) {
Ok(_) => interp_ok(this.eval_windows("c", "TRUE")),
Err(e) => {
this.set_last_error(e)?;
interp_ok(this.eval_windows("c", "FALSE"))
}
}
}
}
/// Windows FILETIME is measured in 100-nanosecs since 1601

View file

@ -1,13 +1,8 @@
#![feature(intrinsics)]
mod rusti {
#[rustc_intrinsic]
pub unsafe fn ctlz_nonzero<T>(x: T) -> u32;
}
#![feature(core_intrinsics)]
pub fn main() {
unsafe {
use crate::rusti::*;
use std::intrinsics::*;
ctlz_nonzero(0u8); //~ ERROR: `ctlz_nonzero` called on 0
}

View file

@ -1,13 +1,8 @@
#![feature(intrinsics)]
mod rusti {
#[rustc_intrinsic]
pub unsafe fn cttz_nonzero<T>(x: T) -> u32;
}
#![feature(core_intrinsics)]
pub fn main() {
unsafe {
use crate::rusti::*;
use std::intrinsics::*;
cttz_nonzero(0u8); //~ ERROR: `cttz_nonzero` called on 0
}

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -1,8 +1,6 @@
#![feature(intrinsics)]
#![feature(core_intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
#[rustc_intrinsic]
unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
use std::intrinsics::float_to_int_unchecked;
fn main() {
unsafe {

View file

@ -2,7 +2,7 @@ error: Miri can only run programs that have a main function.
Alternatively, you can export a `miri_start` function:
#[cfg(miri)]
#[no_mangle]
#[unsafe(no_mangle)]
fn miri_start(argc: isize, argv: *const *const u8) -> isize {
// Call the actual start function that your project implements, based on your target's conventions.
}

View file

@ -9,6 +9,6 @@ extern "C" {
fn main() {
let mut fds = [-1, -1];
let res = unsafe { pipe(fds.as_mut_ptr()) };
//~^ ERROR: calling a non-variadic function with a variadic caller-side signature
//~^ ERROR: ABI mismatch: calling a non-variadic function with a variadic caller-side signature
assert_eq!(res, 0);
}

View file

@ -1,8 +1,8 @@
error: Undefined Behavior: calling a non-variadic function with a variadic caller-side signature
error: Undefined Behavior: ABI mismatch: calling a non-variadic function with a variadic caller-side signature
--> tests/fail/shims/vararg_caller_signature_mismatch.rs:LL:CC
|
LL | let res = unsafe { pipe(fds.as_mut_ptr()) };
| ^^^^^^^^^^^^^^^^^^^^^^ calling a non-variadic function with a variadic caller-side signature
| ^^^^^^^^^^^^^^^^^^^^^^ ABI mismatch: calling a non-variadic function with a variadic caller-side signature
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

View file

@ -2,6 +2,7 @@
//@compile-flags: -Zmiri-disable-isolation
#![allow(nonstandard_style)]
use std::io::ErrorKind;
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use std::ptr;
@ -15,10 +16,10 @@ use windows_sys::Win32::Foundation::{
STATUS_IO_DEVICE_ERROR,
};
use windows_sys::Win32::Storage::FileSystem::{
BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW, CreateFileW, FILE_ATTRIBUTE_DIRECTORY,
FILE_ATTRIBUTE_NORMAL, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT,
FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, GetFileInformationByHandle, OPEN_ALWAYS,
OPEN_EXISTING,
BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW, CreateFileW, DeleteFileW,
FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_NORMAL, FILE_FLAG_BACKUP_SEMANTICS,
FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE,
GetFileInformationByHandle, OPEN_ALWAYS, OPEN_EXISTING,
};
fn main() {
@ -28,6 +29,7 @@ fn main() {
test_create_always_twice();
test_open_always_twice();
test_open_dir_reparse();
test_delete_file();
test_ntstatus_to_dos();
}
}
@ -194,6 +196,21 @@ unsafe fn test_open_dir_reparse() {
};
}
unsafe fn test_delete_file() {
let temp = utils::tmp().join("test_delete_file.txt");
let raw_path = to_wide_cstr(&temp);
let _ = std::fs::File::create(&temp).unwrap();
if DeleteFileW(raw_path.as_ptr()) == 0 {
panic!("Failed to delete file");
}
match std::fs::File::open(temp) {
Ok(_) => panic!("File not deleted"),
Err(e) => assert!(e.kind() == ErrorKind::NotFound, "File not deleted"),
}
}
unsafe fn test_ntstatus_to_dos() {
// We won't test all combinations, just a couple common ones
assert_eq!(RtlNtStatusToDosError(STATUS_IO_DEVICE_ERROR), ERROR_IO_DEVICE);

View file

@ -10,10 +10,10 @@
#![allow(incomplete_features, dead_code)]
// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests
use core::future::{async_drop_in_place, AsyncDrop, Future};
use core::future::{AsyncDrop, Future, async_drop_in_place};
use core::hint::black_box;
use core::mem::{self, ManuallyDrop};
use core::pin::{pin, Pin};
use core::pin::{Pin, pin};
use core::task::{Context, Poll, Waker};
async fn test_async_drop<T>(x: T) {
@ -200,11 +200,7 @@ union AsyncUnion {
impl Drop for AsyncUnion {
fn drop(&mut self) {
println!(
"AsyncUnion::drop: {}, {}",
unsafe { self.signed },
unsafe { self.unsigned },
);
println!("AsyncUnion::drop: {}, {}", unsafe { self.signed }, unsafe { self.unsigned },);
}
}
impl AsyncDrop for AsyncUnion {

View file

@ -17,10 +17,10 @@ mod utils;
fn main() {
test_path_conversion();
test_file_create_new();
// Windows file handling is very incomplete.
if cfg!(not(windows)) {
test_file();
test_file_create_new();
test_seek();
test_file_clone();
test_metadata();

View file

@ -0,0 +1,178 @@
//@revisions: default uniq
//@compile-flags: -Zmiri-tree-borrows
//@[uniq]compile-flags: -Zmiri-unique-is-unique
#![allow(dangerous_implicit_autorefs)]
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
use std::mem::{self, MaybeUninit};
fn main() {
aliasing_mut_and_shr();
aliasing_frz_and_shr();
into_interior_mutability();
unsafe_cell_2phase();
unsafe_cell_deallocate();
unsafe_cell_invalidate();
refcell_basic();
ref_protector();
ref_mut_protector();
rust_issue_68303();
}
fn aliasing_mut_and_shr() {
fn inner(rc: &RefCell<i32>, aliasing: &mut i32) {
*aliasing += 4;
let _escape_to_raw = rc as *const _;
*aliasing += 4;
let _shr = &*rc;
*aliasing += 4;
// also turning this into a frozen ref now must work
let aliasing = &*aliasing;
let _val = *aliasing;
let _escape_to_raw = rc as *const _; // this must NOT unfreeze
let _val = *aliasing;
let _shr = &*rc; // this must NOT unfreeze
let _val = *aliasing;
}
let rc = RefCell::new(23);
let mut bmut = rc.borrow_mut();
inner(&rc, &mut *bmut);
drop(bmut);
assert_eq!(*rc.borrow(), 23 + 12);
}
fn aliasing_frz_and_shr() {
fn inner(rc: &RefCell<i32>, aliasing: &i32) {
let _val = *aliasing;
let _escape_to_raw = rc as *const _; // this must NOT unfreeze
let _val = *aliasing;
let _shr = &*rc; // this must NOT unfreeze
let _val = *aliasing;
}
let rc = RefCell::new(23);
let bshr = rc.borrow();
inner(&rc, &*bshr);
assert_eq!(*rc.borrow(), 23);
}
// Getting a pointer into a union with interior mutability used to be tricky
// business (https://github.com/rust-lang/miri/issues/615), but it should work
// now.
fn into_interior_mutability() {
let mut x: MaybeUninit<(Cell<u32>, u32)> = MaybeUninit::uninit();
x.as_ptr();
x.write((Cell::new(0), 1));
let ptr = unsafe { x.assume_init_ref() };
assert_eq!(ptr.1, 1);
}
// Two-phase borrows of the pointer returned by UnsafeCell::get() should not
// invalidate aliases.
fn unsafe_cell_2phase() {
unsafe {
let x = &UnsafeCell::new(vec![]);
let x2 = &*x;
(*x.get()).push(0);
let _val = (*x2.get()).get(0);
}
}
/// Make sure we can deallocate an UnsafeCell that was passed to an active fn call.
/// (This is the fix for https://github.com/rust-lang/rust/issues/55005.)
fn unsafe_cell_deallocate() {
fn f(x: &UnsafeCell<i32>) {
let b: Box<i32> = unsafe { Box::from_raw(x as *const _ as *mut i32) };
drop(b)
}
let b = Box::new(0i32);
f(unsafe { mem::transmute(Box::into_raw(b)) });
}
/// As a side-effect of the above, we also allow this -- at least for now.
fn unsafe_cell_invalidate() {
fn f(_x: &UnsafeCell<i32>, y: *mut i32) {
// Writing to y invalidates x, but that is okay.
unsafe {
*y += 1;
}
}
let mut x = 0i32;
let raw1 = &mut x as *mut _;
let ref1 = unsafe { &mut *raw1 };
let raw2 = ref1 as *mut _;
// Now the borrow stack is: raw1, ref2, raw2.
// So using raw1 invalidates raw2.
f(unsafe { mem::transmute(raw2) }, raw1);
}
fn refcell_basic() {
let c = RefCell::new(42);
{
let s1 = c.borrow();
let _x: i32 = *s1;
let s2 = c.borrow();
let _x: i32 = *s1;
let _y: i32 = *s2;
let _x: i32 = *s1;
let _y: i32 = *s2;
}
{
let mut m = c.borrow_mut();
let _z: i32 = *m;
{
let s: &i32 = &*m;
let _x = *s;
}
*m = 23;
let _z: i32 = *m;
}
{
let s1 = c.borrow();
let _x: i32 = *s1;
let s2 = c.borrow();
let _x: i32 = *s1;
let _y: i32 = *s2;
let _x: i32 = *s1;
let _y: i32 = *s2;
}
}
// Adding a Stacked Borrows protector for `Ref` would break this
fn ref_protector() {
fn break_it(rc: &RefCell<i32>, r: Ref<'_, i32>) {
// `r` has a shared reference, it is passed in as argument and hence
// a protector is added that marks this memory as read-only for the entire
// duration of this function.
drop(r);
// *oops* here we can mutate that memory.
*rc.borrow_mut() = 2;
}
let rc = RefCell::new(0);
break_it(&rc, rc.borrow())
}
fn ref_mut_protector() {
fn break_it(rc: &RefCell<i32>, r: RefMut<'_, i32>) {
// `r` has a shared reference, it is passed in as argument and hence
// a protector is added that marks this memory as inaccessible for the entire
// duration of this function
drop(r);
// *oops* here we can mutate that memory.
*rc.borrow_mut() = 2;
}
let rc = RefCell::new(0);
break_it(&rc, rc.borrow_mut())
}
/// Make sure we do not have bad enum layout optimizations.
fn rust_issue_68303() {
let optional = Some(RefCell::new(false));
let mut handle = optional.as_ref().unwrap().borrow_mut();
assert!(optional.is_some());
*handle = true;
}