Rollup merge of #65621 - RalfJung:write_bytes, r=oli-obk
miri: add write_bytes method to Memory doing bounds-checks and supporting iterators This lets us avoid some direct `Allocation` accesses in Miri.
This commit is contained in:
commit
e825e21e7a
3 changed files with 39 additions and 20 deletions
|
@ -43,6 +43,7 @@
|
||||||
#![feature(nll)]
|
#![feature(nll)]
|
||||||
#![feature(non_exhaustive)]
|
#![feature(non_exhaustive)]
|
||||||
#![feature(optin_builtin_traits)]
|
#![feature(optin_builtin_traits)]
|
||||||
|
#![feature(option_expect_none)]
|
||||||
#![feature(range_is_empty)]
|
#![feature(range_is_empty)]
|
||||||
#![feature(slice_patterns)]
|
#![feature(slice_patterns)]
|
||||||
#![feature(specialization)]
|
#![feature(specialization)]
|
||||||
|
|
|
@ -245,6 +245,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
|
||||||
/// as a slice.
|
/// as a slice.
|
||||||
///
|
///
|
||||||
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
||||||
|
/// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods
|
||||||
|
/// on `InterpCx` instead.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_bytes(
|
pub fn get_bytes(
|
||||||
&self,
|
&self,
|
||||||
|
@ -275,6 +277,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
|
||||||
/// so be sure to actually put data there!
|
/// so be sure to actually put data there!
|
||||||
///
|
///
|
||||||
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
||||||
|
/// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods
|
||||||
|
/// on `InterpCx` instead.
|
||||||
pub fn get_bytes_mut(
|
pub fn get_bytes_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &impl HasDataLayout,
|
cx: &impl HasDataLayout,
|
||||||
|
@ -297,6 +301,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
|
||||||
impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
|
impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
|
||||||
/// Reads bytes until a `0` is encountered. Will error if the end of the allocation is reached
|
/// Reads bytes until a `0` is encountered. Will error if the end of the allocation is reached
|
||||||
/// before a `0` is found.
|
/// before a `0` is found.
|
||||||
|
///
|
||||||
|
/// Most likely, you want to call `Memory::read_c_str` instead of this method.
|
||||||
pub fn read_c_str(
|
pub fn read_c_str(
|
||||||
&self,
|
&self,
|
||||||
cx: &impl HasDataLayout,
|
cx: &impl HasDataLayout,
|
||||||
|
@ -342,33 +348,22 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
|
||||||
/// Writes `src` to the memory starting at `ptr.offset`.
|
/// Writes `src` to the memory starting at `ptr.offset`.
|
||||||
///
|
///
|
||||||
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
||||||
|
/// Most likely, you want to call `Memory::write_bytes` instead of this method.
|
||||||
pub fn write_bytes(
|
pub fn write_bytes(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &impl HasDataLayout,
|
cx: &impl HasDataLayout,
|
||||||
ptr: Pointer<Tag>,
|
ptr: Pointer<Tag>,
|
||||||
src: &[u8],
|
src: impl IntoIterator<Item=u8, IntoIter: iter::ExactSizeIterator>,
|
||||||
) -> InterpResult<'tcx>
|
) -> InterpResult<'tcx>
|
||||||
{
|
{
|
||||||
|
let mut src = src.into_iter();
|
||||||
let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64))?;
|
let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64))?;
|
||||||
bytes.clone_from_slice(src);
|
// `zip` would stop when the first iterator ends; we want to definitely
|
||||||
Ok(())
|
// cover all of `bytes`.
|
||||||
}
|
for dest in bytes {
|
||||||
|
*dest = src.next().expect("iterator was shorter than it said it would be");
|
||||||
/// Sets `count` bytes starting at `ptr.offset` with `val`. Basically `memset`.
|
|
||||||
///
|
|
||||||
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
|
||||||
pub fn write_repeat(
|
|
||||||
&mut self,
|
|
||||||
cx: &impl HasDataLayout,
|
|
||||||
ptr: Pointer<Tag>,
|
|
||||||
val: u8,
|
|
||||||
count: Size
|
|
||||||
) -> InterpResult<'tcx>
|
|
||||||
{
|
|
||||||
let bytes = self.get_bytes_mut(cx, ptr, count)?;
|
|
||||||
for b in bytes {
|
|
||||||
*b = val;
|
|
||||||
}
|
}
|
||||||
|
src.next().expect_none("iterator was longer than it said it would be");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,6 +375,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
|
||||||
/// pointers being valid for ZSTs.
|
/// pointers being valid for ZSTs.
|
||||||
///
|
///
|
||||||
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
||||||
|
/// Most likely, you want to call `InterpCx::read_scalar` instead of this method.
|
||||||
pub fn read_scalar(
|
pub fn read_scalar(
|
||||||
&self,
|
&self,
|
||||||
cx: &impl HasDataLayout,
|
cx: &impl HasDataLayout,
|
||||||
|
@ -418,6 +414,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
|
||||||
/// Reads a pointer-sized scalar.
|
/// Reads a pointer-sized scalar.
|
||||||
///
|
///
|
||||||
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
||||||
|
/// Most likely, you want to call `InterpCx::read_scalar` instead of this method.
|
||||||
pub fn read_ptr_sized(
|
pub fn read_ptr_sized(
|
||||||
&self,
|
&self,
|
||||||
cx: &impl HasDataLayout,
|
cx: &impl HasDataLayout,
|
||||||
|
@ -435,6 +432,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
|
||||||
/// pointers being valid for ZSTs.
|
/// pointers being valid for ZSTs.
|
||||||
///
|
///
|
||||||
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
||||||
|
/// Most likely, you want to call `InterpCx::write_scalar` instead of this method.
|
||||||
pub fn write_scalar(
|
pub fn write_scalar(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &impl HasDataLayout,
|
cx: &impl HasDataLayout,
|
||||||
|
@ -477,6 +475,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
|
||||||
/// Writes a pointer-sized scalar.
|
/// Writes a pointer-sized scalar.
|
||||||
///
|
///
|
||||||
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
/// It is the caller's responsibility to check bounds and alignment beforehand.
|
||||||
|
/// Most likely, you want to call `InterpCx::write_scalar` instead of this method.
|
||||||
pub fn write_ptr_sized(
|
pub fn write_ptr_sized(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &impl HasDataLayout,
|
cx: &impl HasDataLayout,
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
//! short-circuiting the empty case!
|
//! short-circuiting the empty case!
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::ptr;
|
use std::{ptr, iter};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use rustc::ty::{self, Instance, ParamEnv, query::TyCtxtAt};
|
use rustc::ty::{self, Instance, ParamEnv, query::TyCtxtAt};
|
||||||
|
@ -785,6 +785,25 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
|
||||||
self.get(ptr.alloc_id)?.read_c_str(self, ptr)
|
self.get(ptr.alloc_id)?.read_c_str(self, ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes the given stream of bytes into memory.
|
||||||
|
///
|
||||||
|
/// Performs appropriate bounds checks.
|
||||||
|
pub fn write_bytes(
|
||||||
|
&mut self,
|
||||||
|
ptr: Scalar<M::PointerTag>,
|
||||||
|
src: impl IntoIterator<Item=u8, IntoIter: iter::ExactSizeIterator>,
|
||||||
|
) -> InterpResult<'tcx>
|
||||||
|
{
|
||||||
|
let src = src.into_iter();
|
||||||
|
let size = Size::from_bytes(src.len() as u64);
|
||||||
|
let ptr = match self.check_ptr_access(ptr, size, Align::from_bytes(1).unwrap())? {
|
||||||
|
Some(ptr) => ptr,
|
||||||
|
None => return Ok(()), // zero-sized access
|
||||||
|
};
|
||||||
|
let tcx = self.tcx.tcx;
|
||||||
|
self.get_mut(ptr.alloc_id)?.write_bytes(&tcx, ptr, src)
|
||||||
|
}
|
||||||
|
|
||||||
/// Expects the caller to have checked bounds and alignment.
|
/// Expects the caller to have checked bounds and alignment.
|
||||||
pub fn copy(
|
pub fn copy(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue