From 8b14e9fddaebe1a1fc6463d41f41bd7d73c95487 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 31 May 2017 18:43:09 +0200 Subject: [PATCH] make unsafe more safe --- src/elements/primitives.rs | 2 +- src/interpreter/env.rs | 16 +++--- src/interpreter/module.rs | 6 +- src/interpreter/runner.rs | 6 +- src/interpreter/value.rs | 113 +++++++++++++++++++++++++------------ 5 files changed, 92 insertions(+), 51 deletions(-) diff --git a/src/elements/primitives.rs b/src/elements/primitives.rs index 39b590b..c59821e 100644 --- a/src/elements/primitives.rs +++ b/src/elements/primitives.rs @@ -190,7 +190,7 @@ impl Deserialize for VarInt7 { // expand sign if u8buf[0] & 0b0100_0000 == 0b0100_0000 { u8buf[0] |= 0b1000_0000 } // todo check range - Ok(VarInt7(unsafe { ::std::mem::transmute (u8buf[0]) })) + Ok(VarInt7(u8buf[0] as i8)) } } diff --git a/src/interpreter/env.rs b/src/interpreter/env.rs index 2325da6..d46ed7d 100644 --- a/src/interpreter/env.rs +++ b/src/interpreter/env.rs @@ -9,7 +9,7 @@ use interpreter::module::{ModuleInstanceInterface, ModuleInstance, ExecutionPara ItemIndex, CallerContext}; use interpreter::memory::{MemoryInstance, LINEAR_MEMORY_PAGE_SIZE}; use interpreter::table::TableInstance; -use interpreter::value::{RuntimeValue, TransmuteInto}; +use interpreter::value::RuntimeValue; use interpreter::variable::VariableInstance; /// Memory address, at which stack begins. @@ -169,23 +169,23 @@ pub fn env_module(params: EnvParams) -> Result { .build() .with_export(ExportEntry::new("table".into(), Internal::Table(INDEX_TABLE))) // globals - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(DEFAULT_STACK_BASE.transmute_into())]))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(DEFAULT_STACK_BASE as i32)]))) .with_export(ExportEntry::new("STACK_BASE".into(), Internal::Global(INDEX_GLOBAL_STACK_BASE))) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(DEFAULT_STACK_BASE.transmute_into())]))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(DEFAULT_STACK_BASE as i32)]))) .with_export(ExportEntry::new("STACKTOP".into(), Internal::Global(INDEX_GLOBAL_STACK_TOP))) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack).transmute_into())]))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack) as i32)]))) .with_export(ExportEntry::new("STACK_MAX".into(), Internal::Global(INDEX_GLOBAL_STACK_MAX))) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack).transmute_into())]))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack) as i32)]))) .with_export(ExportEntry::new("DYNAMIC_BASE".into(), Internal::Global(INDEX_GLOBAL_DYNAMIC_BASE))) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack).transmute_into())]))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack) as i32)]))) .with_export(ExportEntry::new("DYNAMICTOP_PTR".into(), Internal::Global(INDEX_GLOBAL_DYNAMICTOP_PTR))) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, params.allow_memory_growth), InitExpr::new(vec![Opcode::I32Const(params.total_memory.transmute_into())]))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, params.allow_memory_growth), InitExpr::new(vec![Opcode::I32Const(params.total_memory as i32)]))) .with_export(ExportEntry::new("TOTAL_MEMORY".into(), Internal::Global(INDEX_GLOBAL_TOTAL_MEMORY))) .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(0)]))) .with_export(ExportEntry::new("ABORT".into(), Internal::Global(INDEX_GLOBAL_ABORT))) .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(0)]))) .with_export(ExportEntry::new("EXITSTATUS".into(), Internal::Global(INDEX_GLOBAL_EXIT_STATUS))) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(DEFAULT_TABLE_BASE.transmute_into())]))) // TODO: what is this? + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(DEFAULT_TABLE_BASE as i32)]))) // TODO: what is this? .with_export(ExportEntry::new("tableBase".into(), Internal::Global(INDEX_GLOBAL_TABLE_BASE))) // functions .function() diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 7e987be..1bf11a5 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -9,7 +9,7 @@ use interpreter::program::ProgramInstanceEssence; use interpreter::runner::{Interpreter, FunctionContext}; use interpreter::stack::StackWithLimit; use interpreter::table::TableInstance; -use interpreter::value::{RuntimeValue, TryInto, TransmuteInto}; +use interpreter::value::{RuntimeValue, TryInto}; use interpreter::variable::{VariableInstance, VariableType}; #[derive(Default, Clone)] @@ -421,8 +421,8 @@ fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports) -> }, &Opcode::I32Const(val) => Ok(RuntimeValue::I32(val)), &Opcode::I64Const(val) => Ok(RuntimeValue::I64(val)), - &Opcode::F32Const(val) => Ok(RuntimeValue::F32(val.transmute_into())), - &Opcode::F64Const(val) => Ok(RuntimeValue::F64(val.transmute_into())), + &Opcode::F32Const(val) => Ok(RuntimeValue::decode_f32(val)), + &Opcode::F64Const(val) => Ok(RuntimeValue::decode_f64(val)), _ => Err(Error::Initialization(format!("not-supported {:?} instruction in instantiation-time initializer", first_opcode))), } } diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index e25a17b..3ae0499 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -8,8 +8,10 @@ use elements::{Opcode, BlockType, FunctionType}; use interpreter::Error; use interpreter::module::{ModuleInstance, ModuleInstanceInterface, CallerContext, ItemIndex}; use interpreter::stack::StackWithLimit; -use interpreter::value::{RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, TransmuteInto, - ArithmeticOps, Integer, Float, LittleEndianConvert}; +use interpreter::value::{ + RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, + ArithmeticOps, Integer, Float, LittleEndianConvert, TransmuteInto, +}; use interpreter::variable::VariableInstance; const DEFAULT_MEMORY_INDEX: u32 = 0; diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 131706f..7931849 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -1,6 +1,5 @@ use std::{i32, i64, u32, u64, f32}; use std::io; -use std::mem; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use interpreter::Error; use interpreter::variable::VariableType; @@ -54,9 +53,9 @@ pub trait TransmuteInto { /// Convert from and to little endian. pub trait LittleEndianConvert where Self: Sized { - /// Convert to little endian bufer. + /// Convert to little endian buffer. fn into_little_endian(self) -> Vec; - /// Convert from little endian bufer. + /// Convert from little endian buffer. fn from_little_endian(buffer: Vec) -> Result; } @@ -124,12 +123,12 @@ impl RuntimeValue { /// Creates new value by interpreting passed u32 as f32. pub fn decode_f32(val: u32) -> Self { - RuntimeValue::F32(val.transmute_into()) + RuntimeValue::F32(f32_from_bits(val)) } /// Creates new value by interpreting passed u64 as f64. pub fn decode_f64(val: u64) -> Self { - RuntimeValue::F64(val.transmute_into()) + RuntimeValue::F64(f64_from_bits(val)) } /// Returns true if value is null. @@ -224,7 +223,6 @@ impl TryInto for RuntimeValue { impl TryInto for RuntimeValue { fn try_into(self) -> Result { match self { - //RuntimeValue::F32(val) => Some(val as f64), RuntimeValue::F64(val) => Ok(val), _ => Err(Error::Value(format!("64-bit float value expected"))), } @@ -234,9 +232,7 @@ impl TryInto for RuntimeValue { impl TryInto for RuntimeValue { fn try_into(self) -> Result { match self { - RuntimeValue::I32(val) => Ok(unsafe { - mem::transmute(val) - }), + RuntimeValue::I32(val) => Ok(val as u32), _ => Err(Error::Value(format!("32-bit int value expected"))), } } @@ -245,9 +241,7 @@ impl TryInto for RuntimeValue { impl TryInto for RuntimeValue { fn try_into(self) -> Result { match self { - RuntimeValue::I64(val) => Ok(unsafe { - mem::transmute(val) - }), + RuntimeValue::I64(val) => Ok(val as u64), _ => Err(Error::Value(format!("64-bit int value expected"))), } } @@ -347,39 +341,48 @@ impl_transmute_into_self!(i64); impl_transmute_into_self!(f32); impl_transmute_into_self!(f64); -macro_rules! impl_transmute_into { +macro_rules! impl_transmute_into_as { ($from: ident, $into: ident) => { impl TransmuteInto<$into> for $from { fn transmute_into(self) -> $into { - unsafe { - mem::transmute(self) - } + self as $into } } } } -impl_transmute_into!(i8, u8); -impl_transmute_into!(u8, i8); -impl_transmute_into!(i32, u32); -impl_transmute_into!(u32, i32); -impl_transmute_into!(u32, f32); -impl_transmute_into!(i32, f32); -impl_transmute_into!(f32, i32); -impl_transmute_into!(i64, u64); -impl_transmute_into!(u64, i64); -impl_transmute_into!(u64, f64); -impl_transmute_into!(i64, f64); -impl_transmute_into!(f64, i64); +impl_transmute_into_as!(i8, u8); +impl_transmute_into_as!(u8, i8); +impl_transmute_into_as!(i32, u32); +impl_transmute_into_as!(u32, i32); +impl_transmute_into_as!(i64, u64); +impl_transmute_into_as!(u64, i64); + +// TODO: rewrite these safely when `f32/f32::to_bits/from_bits` stabilized. +impl TransmuteInto for f32 { + fn transmute_into(self) -> i32 { unsafe { ::std::mem::transmute(self) } } +} + +impl TransmuteInto for f64 { + fn transmute_into(self) -> i64 { unsafe { ::std::mem::transmute(self) } } +} + +impl TransmuteInto for i32 { + fn transmute_into(self) -> f32 { f32_from_bits(self as _) } +} + +impl TransmuteInto for i64 { + fn transmute_into(self) -> f64 { f64_from_bits(self as _) } +} impl LittleEndianConvert for i8 { fn into_little_endian(self) -> Vec { - vec![self.transmute_into()] + vec![self as u8] } fn from_little_endian(buffer: Vec) -> Result { buffer.get(0) - .map(|v| v.transmute_into()) + .map(|v| *v as i8) .ok_or(Error::Value("invalid little endian buffer".into())) } } @@ -469,14 +472,14 @@ impl LittleEndianConvert for i64 { impl LittleEndianConvert for f32 { fn into_little_endian(self) -> Vec { let mut vec = Vec::with_capacity(4); - vec.write_i32::(self.transmute_into()) - .expect("i32 is written without any errors"); + vec.write_f32::(self) + .expect("f32 is written without any errors"); vec } fn from_little_endian(buffer: Vec) -> Result { - io::Cursor::new(buffer).read_i32::() - .map(TransmuteInto::transmute_into) + io::Cursor::new(buffer).read_u32::() + .map(f32_from_bits) .map_err(|e| Error::Value(e.to_string())) } } @@ -484,18 +487,54 @@ impl LittleEndianConvert for f32 { impl LittleEndianConvert for f64 { fn into_little_endian(self) -> Vec { let mut vec = Vec::with_capacity(8); - vec.write_i64::(self.transmute_into()) + vec.write_f64::(self) .expect("i64 is written without any errors"); vec } fn from_little_endian(buffer: Vec) -> Result { - io::Cursor::new(buffer).read_i64::() - .map(TransmuteInto::transmute_into) + io::Cursor::new(buffer).read_u64::() + .map(f64_from_bits) .map_err(|e| Error::Value(e.to_string())) } } +// Convert u32 to f32 safely, masking out sNAN +fn f32_from_bits(mut v: u32) -> f32 { + const EXP_MASK: u32 = 0x7F800000; + const QNAN_MASK: u32 = 0x00400000; + const FRACT_MASK: u32 = 0x007FFFFF; + + if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 { + // If we have a NaN value, we + // convert signaling NaN values to quiet NaN + // by setting the the highest bit of the fraction + // TODO: remove when https://github.com/BurntSushi/byteorder/issues/71 closed. + // or `f32::from_bits` stabilized. + v |= QNAN_MASK; + } + + unsafe { ::std::mem::transmute(v) } +} + +// Convert u64 to f64 safely, masking out sNAN +fn f64_from_bits(mut v: u64) -> f64 { + const EXP_MASK: u64 = 0x7FF0000000000000; + const QNAN_MASK: u64 = 0x0001000000000000; + const FRACT_MASK: u64 = 0x000FFFFFFFFFFFFF; + + if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 { + // If we have a NaN value, we + // convert signaling NaN values to quiet NaN + // by setting the the highest bit of the fraction + // TODO: remove when https://github.com/BurntSushi/byteorder/issues/71 closed. + // or `f64::from_bits` stabilized. + v |= QNAN_MASK; + } + + unsafe { ::std::mem::transmute(v) } +} + macro_rules! impl_integer_arithmetic_ops { ($type: ident) => { impl ArithmeticOps<$type> for $type {