Fix hw1 fuzzer

This commit is contained in:
Jeehoon Kang
2020-03-26 22:59:02 +09:00
parent 8938a7ad8f
commit 938390821f
7 changed files with 178 additions and 69 deletions

View File

@@ -102,6 +102,7 @@ impl IsEquiv for Declarator {
impl IsEquiv for DeclaratorKind { impl IsEquiv for DeclaratorKind {
fn is_equiv(&self, other: &Self) -> bool { fn is_equiv(&self, other: &Self) -> bool {
match (self, other) { match (self, other) {
(Self::Abstract, Self::Abstract) => true,
(Self::Identifier(identifier), Self::Identifier(other_identifier)) => { (Self::Identifier(identifier), Self::Identifier(other_identifier)) => {
identifier.node.name == other_identifier.node.name identifier.node.name == other_identifier.node.name
} }

View File

@@ -525,7 +525,6 @@ impl AssertSupported for Integer {
impl AssertSupported for Float { impl AssertSupported for Float {
fn assert_supported(&self) { fn assert_supported(&self) {
assert_eq!(self.base, FloatBase::Decimal);
self.suffix.format.assert_supported(); self.suffix.format.assert_supported();
assert_eq!(false, self.suffix.imaginary); assert_eq!(false, self.suffix.imaginary);
} }

View File

@@ -11,24 +11,61 @@ use crate::*;
// TODO: the variants of Value will be added in the future // TODO: the variants of Value will be added in the future
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Value { pub enum Value {
Undef,
Unit, Unit,
Int(i32), Int {
Float(f32), value: u128,
Bool(bool), width: usize,
Pointer { bid: Option<usize>, offset: usize }, is_signed: bool,
},
Float {
/// `value` may be `f32`, but it is possible to consider it as `f64`.
///
/// * Casting from an f32 to an f64 is perfect and lossless (f32 -> f64)
/// * Casting from an f64 to an f32 will produce the closest possible value (f64 -> f32)
/// https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#type-cast-expressions
value: f64,
width: usize,
},
Pointer {
bid: Option<usize>,
offset: usize,
},
} }
impl Value { impl Value {
#[inline]
fn unit() -> Self {
Self::Unit
}
#[inline]
pub fn int(value: u128, width: usize, is_signed: bool) -> Self {
Self::Int {
value,
width,
is_signed,
}
}
#[inline]
fn float(value: f64, width: usize) -> Self {
Self::Float { value, width }
}
#[inline] #[inline]
fn pointer(bid: Option<usize>, offset: usize) -> Self { fn pointer(bid: Option<usize>, offset: usize) -> Self {
Self::Pointer { bid, offset } Self::Pointer { bid, offset }
} }
#[inline] #[inline]
fn get_bool(self) -> Option<bool> { fn get_int(self) -> Option<(u128, usize, bool)> {
if let Value::Bool(value) = self { if let Value::Int {
Some(value) value,
width,
is_signed,
} = self
{
Some((value, width, is_signed))
} else { } else {
None None
} }
@@ -54,13 +91,11 @@ impl Value {
#[inline] #[inline]
fn default_from_dtype(dtype: &Dtype) -> Self { fn default_from_dtype(dtype: &Dtype) -> Self {
match dtype { match dtype {
// TODO: consider `Unit` value in the future ir::Dtype::Unit { .. } => Self::unit(),
ir::Dtype::Unit { .. } => todo!(), ir::Dtype::Int {
ir::Dtype::Int { width, .. } => match width { width, is_signed, ..
32 => Self::Int(i32::default()), } => Self::int(u128::default(), *width, *is_signed),
_ => todo!("other cases will be covered"), ir::Dtype::Float { width, .. } => Self::float(f64::default(), *width),
},
ir::Dtype::Float { .. } => Self::Float(f32::default()),
ir::Dtype::Pointer { .. } => Self::nullptr(), ir::Dtype::Pointer { .. } => Self::nullptr(),
ir::Dtype::Function { .. } => panic!("function types do not have a default value"), ir::Dtype::Function { .. } => panic!("function types do not have a default value"),
} }
@@ -183,31 +218,51 @@ mod calculator {
use super::Value; use super::Value;
use lang_c::ast; use lang_c::ast;
// TODO: change to template function in the future
pub fn calculate_binary_operator_expression( pub fn calculate_binary_operator_expression(
op: &ast::BinaryOperator, op: &ast::BinaryOperator,
lhs: Value, lhs: Value,
rhs: Value, rhs: Value,
) -> Result<Value, ()> { ) -> Result<Value, ()> {
match (op, lhs, rhs) { match (op, lhs, rhs) {
(_, Value::Undef, _) => Err(()), (
(_, _, Value::Undef) => Err(()), op,
(ast::BinaryOperator::Plus, Value::Int(lhs), Value::Int(rhs)) => { Value::Int {
Ok(Value::Int(lhs + rhs)) value: lhs,
width: lhs_w,
is_signed: lhs_s,
},
Value::Int {
value: rhs,
width: rhs_w,
is_signed: rhs_s,
},
) => {
assert_eq!(lhs_w, rhs_w);
assert_eq!(lhs_s, rhs_s);
match op {
ast::BinaryOperator::Plus => Ok(Value::int(lhs + rhs, lhs_w, lhs_s)),
ast::BinaryOperator::Minus => Ok(Value::int(lhs - rhs, lhs_w, lhs_s)),
ast::BinaryOperator::Multiply => Ok(Value::int(lhs * rhs, lhs_w, lhs_s)),
ast::BinaryOperator::Equals => {
let result = if lhs == rhs { 1 } else { 0 };
Ok(Value::int(result, 1, lhs_s))
} }
(ast::BinaryOperator::Minus, Value::Int(lhs), Value::Int(rhs)) => { ast::BinaryOperator::NotEquals => {
Ok(Value::Int(lhs - rhs)) let result = if lhs != rhs { 1 } else { 0 };
Ok(Value::int(result, 1, lhs_s))
} }
(ast::BinaryOperator::Equals, Value::Int(lhs), Value::Int(rhs)) => { ast::BinaryOperator::Less => {
Ok(Value::Bool(lhs == rhs)) let result = if lhs < rhs { 1 } else { 0 };
Ok(Value::int(result, 1, lhs_s))
} }
(ast::BinaryOperator::NotEquals, Value::Int(lhs), Value::Int(rhs)) => { ast::BinaryOperator::GreaterOrEqual => {
Ok(Value::Bool(lhs != rhs)) let result = if lhs >= rhs { 1 } else { 0 };
Ok(Value::int(result, 1, lhs_s))
} }
(ast::BinaryOperator::Less, Value::Int(lhs), Value::Int(rhs)) => { _ => todo!("will be covered all operator"),
Ok(Value::Bool(lhs < rhs))
} }
(ast::BinaryOperator::GreaterOrEqual, Value::Int(lhs), Value::Int(rhs)) => {
Ok(Value::Bool(lhs >= rhs))
} }
_ => todo!(), _ => todo!(),
} }
@@ -218,21 +273,57 @@ mod calculator {
operand: Value, operand: Value,
) -> Result<Value, ()> { ) -> Result<Value, ()> {
match (op, operand) { match (op, operand) {
(_, Value::Undef) => Err(()), (
(ast::UnaryOperator::Plus, Value::Int(value)) => Ok(Value::Int(value)), ast::UnaryOperator::Plus,
(ast::UnaryOperator::Minus, Value::Int(value)) => Ok(Value::Int(-value)), Value::Int {
(ast::UnaryOperator::Negate, Value::Bool(value)) => Ok(Value::Bool(!value)), value,
width,
is_signed,
},
) => Ok(Value::int(value, width, is_signed)),
(
ast::UnaryOperator::Minus,
Value::Int {
value,
width,
is_signed,
},
) => {
assert!(is_signed);
let result = -(value as i128);
Ok(Value::int(result as u128, width, is_signed))
}
(
ast::UnaryOperator::Negate,
Value::Int {
value,
width,
is_signed,
},
) => {
// Check if it is boolean
assert!(width == 1);
let result = if value == 0 { 1 } else { 0 };
Ok(Value::int(result, width, is_signed))
}
_ => todo!(), _ => todo!(),
} }
} }
pub fn calculate_typecast(value: &Value, dtype: &crate::ir::Dtype) -> Result<Value, ()> { pub fn calculate_typecast(value: Value, dtype: crate::ir::Dtype) -> Result<Value, ()> {
match (value, dtype) { match (value, dtype) {
(Value::Int(_), crate::ir::Dtype::Int { .. }) => Ok(value.clone()), // TODO: distinguish zero/signed extension in the future
(Value::Bool(b), crate::ir::Dtype::Int { .. }) => { // TODO: consider truncate in the future
Ok(Value::Int(if *b { 1 } else { 0 })) (
Value::Int { value, .. },
crate::ir::Dtype::Int {
width, is_signed, ..
},
) => Ok(Value::int(value, width, is_signed)),
(Value::Float { value, .. }, crate::ir::Dtype::Float { width, .. }) => {
Ok(Value::float(value, width))
} }
_ => todo!("calculate_typecast ({:?}) {:?}", dtype, value), (value, dtype) => todo!("calculate_typecast ({:?}) {:?}", dtype, value),
} }
} }
} }
@@ -246,14 +337,11 @@ struct Memory {
impl Memory { impl Memory {
fn alloc(&mut self, dtype: &Dtype) -> Result<usize, InterpreterError> { fn alloc(&mut self, dtype: &Dtype) -> Result<usize, InterpreterError> {
let memory_block = match dtype { let memory_block = match dtype {
Dtype::Unit { .. } => vec![], ir::Dtype::Unit { .. }
Dtype::Int { width, .. } => match width { | ir::Dtype::Int { .. }
32 => vec![Value::Undef], | ir::Dtype::Float { .. }
_ => todo!(), | ir::Dtype::Pointer { .. } => vec![Value::default_from_dtype(dtype)],
}, ir::Dtype::Function { .. } => vec![],
Dtype::Float { .. } => todo!(),
Dtype::Pointer { .. } => vec![Value::Undef],
Dtype::Function { .. } => vec![],
}; };
self.inner.push(memory_block); self.inner.push(memory_block);
@@ -327,14 +415,15 @@ impl<'i> State<'i> {
// Initialize allocated memory space // Initialize allocated memory space
match decl { match decl {
Declaration::Variable { dtype, initializer } => { Declaration::Variable { dtype, initializer } => {
let value = if let Some(constant) = initializer { if dtype.get_function_inner().is_some() {
self.interp_constant(constant.clone()) panic!("function variable does not exist")
} else { }
Value::default_from_dtype(dtype)
};
if let Some(constant) = initializer {
let value = self.interp_constant(constant.clone());
self.memory.store(bid, 0, value); self.memory.store(bid, 0, value);
} }
}
// If functin declaration, skip initialization // If functin declaration, skip initialization
Declaration::Function { .. } => (), Declaration::Function { .. } => (),
} }
@@ -461,8 +550,11 @@ impl<'i> State<'i> {
arg_else, arg_else,
} => { } => {
let value = self.interp_operand(condition.clone())?; let value = self.interp_operand(condition.clone())?;
let value = value.get_bool().expect("`condition` must be `Value::Bool`"); let (value, width, _) = value.get_int().expect("`condition` must be `Value::Int`");
self.interp_jump(if value { arg_then } else { arg_else }) // Check if it is boolean
assert!(width == 1);
self.interp_jump(if value == 1 { arg_then } else { arg_else })
} }
BlockExit::Switch { BlockExit::Switch {
value, value,
@@ -565,7 +657,7 @@ impl<'i> State<'i> {
target_dtype, target_dtype,
} => { } => {
let value = self.interp_operand(value.clone())?; let value = self.interp_operand(value.clone())?;
calculator::calculate_typecast(&value, target_dtype).map_err(|_| { calculator::calculate_typecast(value, target_dtype.clone()).map_err(|_| {
InterpreterError::Misc { InterpreterError::Misc {
func_name: self.stack_frame.func_name.clone(), func_name: self.stack_frame.func_name.clone(),
pc: self.stack_frame.pc, pc: self.stack_frame.pc,
@@ -594,9 +686,16 @@ impl<'i> State<'i> {
fn interp_constant(&self, value: Constant) -> Value { fn interp_constant(&self, value: Constant) -> Value {
match value { match value {
Constant::Unit => Value::Unit, Constant::Unit => Value::Unit,
// TODO: consider `width` and `is_signed` in the future Constant::Int {
Constant::Int { value, .. } => Value::Int(value as i32), value,
Constant::Float { value, .. } => Value::Float(value as f32), width,
is_signed,
} => Value::Int {
value,
width,
is_signed,
},
Constant::Float { value, width } => Value::Float { value, width },
Constant::GlobalVariable { name, .. } => { Constant::GlobalVariable { name, .. } => {
let bid = self let bid = self
.global_map .global_map

View File

@@ -52,5 +52,8 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) {
.expect("failed to generate ir"); .expect("failed to generate ir");
let args = Vec::new(); let args = Vec::new();
assert_eq!(ir::interp(&ir, args), Ok(ir::Value::Int(status))); assert_eq!(
ir::interp(&ir, args),
Ok(ir::Value::int(status as u128, 32, true))
);
} }

2
tests/.gitignore vendored
View File

@@ -1,4 +1,4 @@
/platform.info /platform.info
/csmith-* /csmith-*
/test*.c* /test*.c
/reduce-criteria.sh /reduce-criteria.sh

View File

@@ -22,13 +22,18 @@ REPLACE_DICT = {
"__restrict": "", "__restrict": "",
"long __undefined;": "", "long __undefined;": "",
"return 0;": "return crc32_context % 128;", "return 0;": "return crc32_context % 128;",
r"__attribute__ \(\(.*\)\)": "", r"__attribute__\s*\(\(.*\)\)": "",
"_Float128": "long double", "_Float128": "long double",
"union": "struct", "union": "struct",
r"enum\s*\{[^\}]*\};": "", r"enum[\w\s]*\{[^\}]*\};": "",
"const char \*const sys_errlist\[\];": "", "const char \*const sys_errlist\[\];": "",
r"[^\n]*printf[^;]*;": "", r"[^\n]*printf[^;]*;": "",
r"[^\n]*scanf[^;]*;": "", r"[^\n]*scanf[^;]*;": "",
" restrict": "",
"inline ": "",
"_Nullable": "",
"\"g_\w*\", ": "",
"char\* vname, ": "",
} }
CSMITH_DIR = "csmith-2.3.0" CSMITH_DIR = "csmith-2.3.0"
@@ -172,7 +177,8 @@ def creduce(tests_dir, fuzz_arg):
raise e raise e
try: try:
args = ["creduce", "./reduce-criteria.sh", "test_reduced.c"] # --tidy: Do not make a backup copy of each file to reduce as file.orig
args = ["creduce", "--tidy", "./reduce-criteria.sh", "test_reduced.c"]
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=tests_dir) proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=tests_dir)
(out, err) = proc.communicate() (out, err) = proc.communicate()
if proc.returncode != 0: if proc.returncode != 0:

View File

@@ -1,3 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
cargo run --manifest-path $PROJECT_DIR/Cargo.toml --release -- -p test_reduced.c >/dev/null 2>&1 &&\
! cargo run --manifest-path $PROJECT_DIR/Cargo.toml --release --bin fuzz -- $FUZZ_ARG test_reduced.c ! cargo run --manifest-path $PROJECT_DIR/Cargo.toml --release --bin fuzz -- $FUZZ_ARG test_reduced.c