Auto merge of #140436 - RalfJung:miri-sync, r=RalfJung
Miri subtree update r? `@ghost`
This commit is contained in:
commit
efcbb94dcc
47 changed files with 802 additions and 307 deletions
78
src/tools/miri/.github/workflows/ci.yml
vendored
78
src/tools/miri/.github/workflows/ci.yml
vendored
|
|
@ -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 }}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
1bc56185ee257ed829a0aea7abdc3b03c5fed887
|
||||
1b8ab72680f36e783af84c1a3c4f8508572bd9f9
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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\
|
||||
}"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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" => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
178
src/tools/miri/tests/pass/tree_borrows/interior_mutability.rs
Normal file
178
src/tools/miri/tests/pass/tree_borrows/interior_mutability.rs
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue