#![allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::cast_precision_loss,
clippy::cast_lossless
)]
use crate::{
emulation::{
engine::EmulationError,
value::{ConversionType, EmValue, SymbolicValue, TaintSource},
},
metadata::typesystem::PointerSize,
Result,
};
impl EmValue {
pub fn convert(&self, conv: ConversionType, ptr_size: PointerSize) -> Result<Self> {
if let EmValue::Symbolic(_) = self {
return Ok(EmValue::Symbolic(SymbolicValue::derived(
conv.target_cil_flavor(),
TaintSource::Computation,
)));
}
match conv {
ConversionType::I1 => self.conv_i1(),
ConversionType::I2 => self.conv_i2(),
ConversionType::I4 => self.conv_i4(),
ConversionType::I8 => self.conv_i8(),
ConversionType::U1 => self.conv_u1(),
ConversionType::U2 => self.conv_u2(),
ConversionType::U4 => self.conv_u4(),
ConversionType::U8 => self.conv_u8(),
ConversionType::R4 => self.conv_r4(),
ConversionType::R8 => self.conv_r8(),
ConversionType::I => self.conv_i(ptr_size),
ConversionType::U => self.conv_u(ptr_size),
ConversionType::RUn => self.conv_r_un(),
ConversionType::I1Ovf => self.conv_i1_ovf(false),
ConversionType::I2Ovf => self.conv_i2_ovf(false),
ConversionType::I4Ovf => self.conv_i4_ovf(false),
ConversionType::I8Ovf => self.conv_i8_ovf(false),
ConversionType::U1Ovf => self.conv_u1_ovf(false),
ConversionType::U2Ovf => self.conv_u2_ovf(false),
ConversionType::U4Ovf => self.conv_u4_ovf(false),
ConversionType::U8Ovf => self.conv_u8_ovf(false),
ConversionType::IOvf => self.conv_i_ovf(false),
ConversionType::UOvf => self.conv_u_ovf(false),
ConversionType::I1OvfUn => self.conv_i1_ovf(true),
ConversionType::I2OvfUn => self.conv_i2_ovf(true),
ConversionType::I4OvfUn => self.conv_i4_ovf(true),
ConversionType::I8OvfUn => self.conv_i8_ovf(true),
ConversionType::U1OvfUn => self.conv_u1_ovf(true),
ConversionType::U2OvfUn => self.conv_u2_ovf(true),
ConversionType::U4OvfUn => self.conv_u4_ovf(true),
ConversionType::U8OvfUn => self.conv_u8_ovf(true),
ConversionType::IOvfUn => self.conv_i_ovf(true),
ConversionType::UOvfUn => self.conv_u_ovf(true),
}
}
fn conv_i1(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I32(value as i8 as i32))
}
fn conv_i2(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I32(value as i16 as i32))
}
fn conv_i4(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I32(value as i32))
}
fn conv_i8(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I64(value))
}
fn conv_u1(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I32((value as u8) as i32))
}
fn conv_u2(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I32((value as u16) as i32))
}
fn conv_u4(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I32(value as u32 as i32))
}
fn conv_u8(&self) -> Result<Self> {
let value = self.to_u64()?;
Ok(EmValue::I64(value as i64))
}
fn conv_r4(&self) -> Result<Self> {
match *self {
EmValue::I32(v) => Ok(EmValue::F32(v as f32)),
EmValue::I64(v) | EmValue::NativeInt(v) => Ok(EmValue::F32(v as f32)),
EmValue::NativeUInt(v) => Ok(EmValue::F32(v as f32)),
EmValue::F32(v) => Ok(EmValue::F32(v)),
EmValue::F64(v) => Ok(EmValue::F32(v as f32)),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "conv.r4".to_string(),
operand_types: self.cil_flavor().to_string(),
}
.into()),
}
}
fn conv_r8(&self) -> Result<Self> {
match *self {
EmValue::I32(v) => Ok(EmValue::F64(f64::from(v))),
EmValue::I64(v) | EmValue::NativeInt(v) => Ok(EmValue::F64(v as f64)),
EmValue::NativeUInt(v) => Ok(EmValue::F64(v as f64)),
EmValue::F32(v) => Ok(EmValue::F64(f64::from(v))),
EmValue::F64(v) => Ok(EmValue::F64(v)),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "conv.r8".to_string(),
operand_types: self.cil_flavor().to_string(),
}
.into()),
}
}
fn conv_i(&self, ptr_size: PointerSize) -> Result<Self> {
let value = ptr_size.mask_signed(self.to_i64()?);
Ok(EmValue::NativeInt(value))
}
fn conv_u(&self, ptr_size: PointerSize) -> Result<Self> {
let value = ptr_size.mask_unsigned(self.to_u64()?);
Ok(EmValue::NativeUInt(value))
}
fn conv_r_un(&self) -> Result<Self> {
let value = self.to_u64()?;
Ok(EmValue::F64(value as f64))
}
fn conv_i1_ovf(&self, unsigned_source: bool) -> Result<Self> {
let value = if unsigned_source {
self.to_u64()? as i64
} else {
self.to_i64()?
};
if value < i8::MIN as i64 || value > i8::MAX as i64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(value as i32))
}
}
fn conv_i2_ovf(&self, unsigned_source: bool) -> Result<Self> {
let value = if unsigned_source {
self.to_u64()? as i64
} else {
self.to_i64()?
};
if value < i16::MIN as i64 || value > i16::MAX as i64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(value as i32))
}
}
fn conv_i4_ovf(&self, unsigned_source: bool) -> Result<Self> {
let value = if unsigned_source {
self.to_u64()? as i64
} else {
self.to_i64()?
};
if value < i32::MIN as i64 || value > i32::MAX as i64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(value as i32))
}
}
fn conv_i8_ovf(&self, unsigned_source: bool) -> Result<Self> {
if unsigned_source {
let value = self.to_u64()?;
if value > i64::MAX as u64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I64(value as i64))
}
} else {
let value = self.to_i64()?;
Ok(EmValue::I64(value))
}
}
fn conv_u1_ovf(&self, unsigned_source: bool) -> Result<Self> {
let value = if unsigned_source {
self.to_u64()?
} else {
let signed = self.to_i64()?;
if signed < 0 {
return Err(EmulationError::ArithmeticOverflow.into());
}
signed as u64
};
if value > u8::MAX as u64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(value as i32))
}
}
fn conv_u2_ovf(&self, unsigned_source: bool) -> Result<Self> {
let value = if unsigned_source {
self.to_u64()?
} else {
let signed = self.to_i64()?;
if signed < 0 {
return Err(EmulationError::ArithmeticOverflow.into());
}
signed as u64
};
if value > u16::MAX as u64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(value as i32))
}
}
fn conv_u4_ovf(&self, unsigned_source: bool) -> Result<Self> {
let value = if unsigned_source {
self.to_u64()?
} else {
let signed = self.to_i64()?;
if signed < 0 {
return Err(EmulationError::ArithmeticOverflow.into());
}
signed as u64
};
if value > u32::MAX as u64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(value as u32 as i32))
}
}
fn conv_u8_ovf(&self, unsigned_source: bool) -> Result<Self> {
if unsigned_source {
let value = self.to_u64()?;
Ok(EmValue::I64(value as i64))
} else {
let value = self.to_i64()?;
if value < 0 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I64(value))
}
}
}
fn conv_i_ovf(&self, unsigned_source: bool) -> Result<Self> {
self.conv_i8_ovf(unsigned_source).map(|v| match v {
EmValue::I64(n) => EmValue::NativeInt(n),
other => other,
})
}
fn conv_u_ovf(&self, unsigned_source: bool) -> Result<Self> {
if unsigned_source {
let value = self.to_u64()?;
Ok(EmValue::NativeUInt(value))
} else {
let value = self.to_i64()?;
if value < 0 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::NativeUInt(value as u64))
}
}
}
fn to_i64(&self) -> Result<i64> {
match self {
EmValue::I32(v) => Ok(i64::from(*v)),
EmValue::I64(v) | EmValue::NativeInt(v) => Ok(*v),
EmValue::NativeUInt(v) => Ok(*v as i64),
EmValue::Bool(v) => Ok(i64::from(*v)),
EmValue::Char(v) => Ok(*v as i64),
EmValue::F32(v) => Ok(*v as i64),
EmValue::F64(v) => Ok(*v as i64),
EmValue::UnmanagedPtr(addr) => Ok(*addr as i64),
EmValue::ManagedPtr(ptr) => Ok(ptr.synthesize_address() as i64),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "conversion".to_string(),
operand_types: self.cil_flavor().to_string(),
}
.into()),
}
}
fn to_u64(&self) -> Result<u64> {
match self {
EmValue::I32(v) => Ok(*v as u32 as u64),
EmValue::I64(v) | EmValue::NativeInt(v) => Ok(*v as u64),
EmValue::NativeUInt(v) => Ok(*v),
EmValue::Bool(v) => Ok(u64::from(*v)),
EmValue::Char(v) => Ok(*v as u64),
EmValue::F32(v) => Ok(*v as u64),
EmValue::F64(v) => Ok(*v as u64),
EmValue::UnmanagedPtr(addr) => Ok(*addr),
EmValue::ManagedPtr(ptr) => Ok(ptr.synthesize_address()),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "conversion".to_string(),
operand_types: self.cil_flavor().to_string(),
}
.into()),
}
}
}
#[cfg(test)]
mod tests {
use crate::{
emulation::{
engine::EmulationError,
value::{ConversionType, EmValue},
},
metadata::typesystem::PointerSize,
Error,
};
#[test]
fn test_conv_i1() {
let a = EmValue::I32(300);
let result = a.convert(ConversionType::I1, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::I32(44));
let b = EmValue::I32(-1);
let result = b.convert(ConversionType::I1, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::I32(-1));
}
#[test]
fn test_conv_u1() {
let a = EmValue::I32(-1);
let result = a.convert(ConversionType::U1, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::I32(255));
}
#[test]
fn test_conv_i8() {
let a = EmValue::I32(42);
let result = a.convert(ConversionType::I8, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::I64(42));
}
#[test]
fn test_conv_u8() {
let a = EmValue::I32(-1);
let result = a.convert(ConversionType::U8, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::I64(0xFFFFFFFF));
}
#[test]
fn test_conv_r4() {
let a = EmValue::I32(42);
let result = a.convert(ConversionType::R4, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::F32(42.0));
}
#[test]
fn test_conv_r8() {
let a = EmValue::I32(42);
let result = a.convert(ConversionType::R8, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::F64(42.0));
}
#[test]
fn test_conv_ovf_i1() {
let a = EmValue::I32(200);
assert!(matches!(
a.convert(ConversionType::I1Ovf, PointerSize::Bit64),
Err(Error::Emulation(ref e)) if matches!(e.as_ref(), EmulationError::ArithmeticOverflow)
));
}
#[test]
fn test_conv_ovf_u1() {
let a = EmValue::I32(-1);
assert!(matches!(
a.convert(ConversionType::U1Ovf, PointerSize::Bit64),
Err(Error::Emulation(ref e)) if matches!(e.as_ref(), EmulationError::ArithmeticOverflow)
));
}
#[test]
fn test_conv_r_un() {
let a = EmValue::I32(-1);
let result = a.convert(ConversionType::RUn, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::F64(4294967295.0));
}
}