Skip to content

Commit 197c492

Browse files
committed
forward some undefined bytes transparently
this allows things like `Foo { a: std::mem::uninitialized() }`. mitgates #95
1 parent e4b23b8 commit 197c492

File tree

5 files changed

+65
-44
lines changed

5 files changed

+65
-44
lines changed

src/eval_context.rs

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -370,9 +370,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
370370

371371
for (offset, operand) in offsets.into_iter().zip(operands) {
372372
let value = self.eval_operand(operand)?;
373-
let value_ty = self.operand_ty(operand);
374-
let field_dest = dest.offset(offset);
375-
self.write_value_to_ptr(value, field_dest, value_ty)?;
373+
if let Some(value) = value {
374+
let value_ty = self.operand_ty(operand);
375+
let field_dest = dest.offset(offset);
376+
self.write_value_to_ptr(value, field_dest, value_ty)?;
377+
}
376378
}
377379
Ok(())
378380
}
@@ -393,8 +395,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
393395
use rustc::mir::Rvalue::*;
394396
match *rvalue {
395397
Use(ref operand) => {
396-
let value = self.eval_operand(operand)?;
397-
self.write_value(value, dest, dest_ty)?;
398+
if let Some(value) = self.eval_operand(operand)? {
399+
self.write_value(value, dest, dest_ty)?;
400+
}
398401
}
399402

400403
BinaryOp(bin_op, ref left, ref right) => {
@@ -456,7 +459,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
456459
if nndiscr == variant as u64 {
457460
assert_eq!(operands.len(), 1);
458461
let operand = &operands[0];
459-
let value = self.eval_operand(operand)?;
462+
let value = self.eval_operand(operand)?.ok_or(EvalError::ReadUndefBytes)?;
460463
let value_ty = self.operand_ty(operand);
461464
self.write_value(value, dest, value_ty)?;
462465
} else {
@@ -516,7 +519,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
516519
UntaggedUnion { .. } => {
517520
assert_eq!(operands.len(), 1);
518521
let operand = &operands[0];
519-
let value = self.eval_operand(operand)?;
522+
let value = self.eval_operand(operand)?.ok_or(EvalError::ReadUndefBytes)?;
520523
let value_ty = self.operand_ty(operand);
521524

522525
// FIXME(solson)
@@ -536,20 +539,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
536539
}
537540

538541
Repeat(ref operand, _) => {
539-
let (elem_ty, length) = match dest_ty.sty {
540-
ty::TyArray(elem_ty, n) => (elem_ty, n as u64),
541-
_ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty),
542-
};
543-
self.inc_step_counter_and_check_limit(length)?;
544-
let elem_size = self.type_size(elem_ty)?.expect("repeat element type must be sized");
545-
let value = self.eval_operand(operand)?;
546-
547-
// FIXME(solson)
548-
let dest = self.force_allocation(dest)?.to_ptr();
549-
550-
for i in 0..length {
551-
let elem_dest = dest.offset(i * elem_size);
552-
self.write_value_to_ptr(value, elem_dest, elem_ty)?;
542+
if let Some(value) = self.eval_operand(operand)? {
543+
let (elem_ty, length) = match dest_ty.sty {
544+
ty::TyArray(elem_ty, n) => (elem_ty, n as u64),
545+
_ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty),
546+
};
547+
self.inc_step_counter_and_check_limit(length)?;
548+
let elem_size = self.type_size(elem_ty)?.expect("repeat element type must be sized");
549+
// FIXME(solson)
550+
let dest = self.force_allocation(dest)?.to_ptr();
551+
552+
for i in 0..length {
553+
let elem_dest = dest.offset(i * elem_size);
554+
self.write_value_to_ptr(value, elem_dest, elem_ty)?;
555+
}
553556
}
554557
}
555558

@@ -586,13 +589,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
586589
use rustc::mir::CastKind::*;
587590
match kind {
588591
Unsize => {
589-
let src = self.eval_operand(operand)?;
592+
let src = self.eval_operand(operand)?.ok_or(EvalError::ReadUndefBytes)?;
590593
let src_ty = self.operand_ty(operand);
591594
self.unsize_into(src, src_ty, dest, dest_ty)?;
592595
}
593596

594597
Misc => {
595-
let src = self.eval_operand(operand)?;
598+
let src = self.eval_operand(operand)?.ok_or(EvalError::ReadUndefBytes)?;
596599
let src_ty = self.operand_ty(operand);
597600
if self.type_is_fat_ptr(src_ty) {
598601
trace!("misc cast: {:?}", src);
@@ -624,7 +627,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
624627

625628
UnsafeFnPointer => match dest_ty.sty {
626629
ty::TyFnPtr(unsafe_fn_ty) => {
627-
let src = self.eval_operand(operand)?;
630+
let src = self.eval_operand(operand)?.ok_or(EvalError::ReadUndefBytes)?;
628631
let ptr = src.read_ptr(&self.memory)?;
629632
let (def_id, substs, _, _) = self.memory.get_fn(ptr.alloc_id)?;
630633
let unsafe_fn_ty = self.tcx.erase_regions(&unsafe_fn_ty);
@@ -749,12 +752,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
749752
}
750753

751754
pub(super) fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> {
752-
let value = self.eval_operand(op)?;
755+
let value = self.eval_operand(op)?.ok_or(EvalError::ReadUndefBytes)?;
753756
let ty = self.operand_ty(op);
754757
self.value_to_primval(value, ty)
755758
}
756759

757-
pub(super) fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> {
760+
pub(super) fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Option<Value>> {
758761
use rustc::mir::Operand::*;
759762
match *op {
760763
Consume(ref lvalue) => self.eval_and_read_lvalue(lvalue),
@@ -775,6 +778,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
775778
promoted: None,
776779
};
777780
self.read_lvalue(Lvalue::Global(cid))?
781+
.expect("constants and statics can't be uninitialized")
778782
}
779783
}
780784

@@ -785,10 +789,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
785789
promoted: Some(index),
786790
};
787791
self.read_lvalue(Lvalue::Global(cid))?
792+
.expect("promoteds can't be uninitialized")
788793
}
789794
};
790795

791-
Ok(value)
796+
Ok(Some(value))
792797
}
793798
}
794799
}

src/lvalue.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,14 @@ impl<'tcx> Global<'tcx> {
105105
}
106106

107107
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
108-
pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> {
108+
/// if the lvalue is uninitialized, this function returns `None`
109+
pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Option<Value>> {
109110
if let mir::Lvalue::Projection(ref proj) = *lvalue {
110111
if let mir::Lvalue::Local(index) = proj.base {
111112
if let Some(Value::ByValPair(a, b)) = self.frame().get_local(index) {
112113
if let mir::ProjectionElem::Field(ref field, _) = proj.elem {
113114
let val = [a, b][field.index()];
114-
return Ok(Value::ByVal(val));
115+
return Ok(Some(Value::ByVal(val)));
115116
}
116117
}
117118
}
@@ -120,20 +121,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
120121
self.read_lvalue(lvalue)
121122
}
122123

123-
pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Value> {
124+
/// if the lvalue is uninitialized, this function returns `None`
125+
pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Option<Value>> {
124126
match lvalue {
125127
Lvalue::Ptr { ptr, extra } => {
126128
assert_eq!(extra, LvalueExtra::None);
127-
Ok(Value::ByRef(ptr))
129+
Ok(Some(Value::ByRef(ptr)))
128130
}
129131
Lvalue::Local { frame, local } => {
130-
self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes)
132+
Ok(self.stack[frame].get_local(local))
131133
}
132-
Lvalue::Global(cid) => self.globals
133-
.get(&cid)
134-
.expect("global not cached")
135-
.data
136-
.ok_or(EvalError::ReadUndefBytes),
134+
Lvalue::Global(cid) => Ok(self.globals
135+
.get(&cid)
136+
.expect("global not cached")
137+
.data)
137138
}
138139
}
139140

@@ -246,7 +247,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
246247
}
247248

248249
Deref => {
249-
let val = self.eval_and_read_lvalue(&proj.base)?;
250+
let val = self.eval_and_read_lvalue(&proj.base)?.ok_or(EvalError::ReadUndefBytes)?;
250251

251252
let pointee_type = match base_ty.sty {
252253
ty::TyRawPtr(ty::TypeAndMut{ty, ..}) |
@@ -277,7 +278,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
277278

278279
let (elem_ty, len) = base.elem_ty_and_len(base_ty);
279280
let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
280-
let n_ptr = self.eval_operand(operand)?;
281+
let n_ptr = self.eval_operand(operand)?.ok_or(EvalError::ReadUndefBytes)?;
281282
let usize = self.tcx.types.usize;
282283
let n = self.value_to_primval(n_ptr, usize)?.to_u64();
283284
assert!(n < len);

src/terminator/intrinsic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
2222
target: mir::BasicBlock,
2323
) -> EvalResult<'tcx, ()> {
2424
let arg_vals: EvalResult<Vec<Value>> = args.iter()
25-
.map(|arg| self.eval_operand(arg))
25+
.map(|arg| self.eval_operand(arg)?.ok_or(EvalError::ReadUndefBytes))
2626
.collect();
2727
let arg_vals = arg_vals?;
2828
let i32 = self.tcx.types.i32;

src/terminator/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
4343
}
4444

4545
SwitchInt { ref discr, ref values, ref targets, .. } => {
46-
let discr_val = self.eval_and_read_lvalue(discr)?;
46+
let discr_val = self.eval_and_read_lvalue(discr)?.ok_or(EvalError::ReadUndefBytes)?;
4747
let discr_ty = self.lvalue_ty(discr);
4848
let discr_prim = self.value_to_primval(discr_val, discr_ty)?;
4949

@@ -216,7 +216,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
216216
Abi::Rust | Abi::RustCall => {
217217
let mut args = Vec::new();
218218
for arg in arg_operands {
219-
let arg_val = self.eval_operand(arg)?;
219+
let arg_val = self.eval_operand(arg)?.ok_or(EvalError::ReadUndefBytes)?;
220220
let arg_ty = self.operand_ty(arg);
221221
args.push((arg_val, arg_ty));
222222
}
@@ -353,7 +353,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
353353
.as_str();
354354

355355
let args_res: EvalResult<Vec<Value>> = args.iter()
356-
.map(|arg| self.eval_operand(arg))
356+
.map(|arg| self.eval_operand(arg)?.ok_or(EvalError::ReadUndefBytes))
357357
.collect();
358358
let args = args_res?;
359359

@@ -613,7 +613,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
613613
match ty.sty {
614614
// special case `Box` to deallocate the inner allocation
615615
ty::TyBox(contents_ty) => {
616-
let val = self.read_lvalue(lval)?;
616+
let val = self.read_lvalue(lval)?.ok_or(EvalError::ReadUndefBytes)?;
617617
// we are going through the read_value path, because that already does all the
618618
// checks for the trait object types. We'd only be repeating ourselves here.
619619
let val = self.follow_by_ref_value(val, ty)?;

tests/run-pass/uninitialized.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![allow(dead_code)]
2+
#![allow(unused)]
3+
4+
pub struct Foo {
5+
inner: i32,
6+
}
7+
8+
fn main() {
9+
unsafe {
10+
let foo = Foo {
11+
inner: std::mem::uninitialized(),
12+
};
13+
let bar = foo;
14+
}
15+
}

0 commit comments

Comments
 (0)