use crate::{
emulation::{
engine::EmulationError,
value::{EmValue, SymbolicValue, TaintSource, UnaryOp},
},
metadata::typesystem::PointerSize,
Result,
};
impl EmValue {
pub fn unary_op(&self, op: UnaryOp, ptr_size: PointerSize) -> Result<Self> {
if let EmValue::Symbolic(ref sym) = self {
return Ok(EmValue::Symbolic(SymbolicValue::derived(
sym.cil_flavor.clone(),
TaintSource::Computation,
)));
}
let result = match op {
UnaryOp::Neg => self.neg(),
UnaryOp::Not => self.not(),
}?;
Ok(match result {
EmValue::NativeInt(v) => EmValue::NativeInt(ptr_size.mask_signed(v)),
EmValue::NativeUInt(v) => EmValue::NativeUInt(ptr_size.mask_unsigned(v)),
other => other,
})
}
fn neg(&self) -> Result<Self> {
match *self {
EmValue::I32(a) => Ok(EmValue::I32(a.wrapping_neg())),
EmValue::I64(a) => Ok(EmValue::I64(a.wrapping_neg())),
EmValue::NativeInt(a) => Ok(EmValue::NativeInt(a.wrapping_neg())),
EmValue::F32(a) => Ok(EmValue::F32(-a)),
EmValue::F64(a) => Ok(EmValue::F64(-a)),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "neg".to_string(),
operand_types: self.cil_flavor().to_string(),
}
.into()),
}
}
fn not(&self) -> Result<Self> {
match *self {
EmValue::I32(a) => Ok(EmValue::I32(!a)),
EmValue::I64(a) => Ok(EmValue::I64(!a)),
EmValue::NativeInt(a) => Ok(EmValue::NativeInt(!a)),
EmValue::NativeUInt(a) => Ok(EmValue::NativeUInt(!a)),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "not".to_string(),
operand_types: self.cil_flavor().to_string(),
}
.into()),
}
}
}
#[cfg(test)]
mod tests {
use crate::{
emulation::value::{EmValue, UnaryOp},
metadata::typesystem::PointerSize,
};
#[test]
fn test_unary_neg_i32() {
let a = EmValue::I32(42);
assert_eq!(
a.unary_op(UnaryOp::Neg, PointerSize::Bit64).unwrap(),
EmValue::I32(-42)
);
}
#[test]
fn test_unary_neg_f64() {
let a = EmValue::F64(std::f64::consts::PI);
assert_eq!(
a.unary_op(UnaryOp::Neg, PointerSize::Bit64).unwrap(),
EmValue::F64(-std::f64::consts::PI)
);
}
#[test]
fn test_unary_not_i32() {
let a = EmValue::I32(0);
assert_eq!(
a.unary_op(UnaryOp::Not, PointerSize::Bit64).unwrap(),
EmValue::I32(-1)
);
}
#[test]
fn test_unary_not_pattern() {
let a = EmValue::I32(0x0F0F0F0F);
assert_eq!(
a.unary_op(UnaryOp::Not, PointerSize::Bit64).unwrap(),
EmValue::I32(0xF0F0F0F0_u32 as i32)
);
}
}