mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-16 07:28:52 +00:00
Fix hw1 fuzzer
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
225
src/ir/interp.rs
225
src/ir/interp.rs
@@ -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,
|
||||||
(ast::BinaryOperator::Minus, Value::Int(lhs), Value::Int(rhs)) => {
|
is_signed: lhs_s,
|
||||||
Ok(Value::Int(lhs - rhs))
|
},
|
||||||
}
|
Value::Int {
|
||||||
(ast::BinaryOperator::Equals, Value::Int(lhs), Value::Int(rhs)) => {
|
value: rhs,
|
||||||
Ok(Value::Bool(lhs == rhs))
|
width: rhs_w,
|
||||||
}
|
is_signed: rhs_s,
|
||||||
(ast::BinaryOperator::NotEquals, Value::Int(lhs), Value::Int(rhs)) => {
|
},
|
||||||
Ok(Value::Bool(lhs != rhs))
|
) => {
|
||||||
}
|
assert_eq!(lhs_w, rhs_w);
|
||||||
(ast::BinaryOperator::Less, Value::Int(lhs), Value::Int(rhs)) => {
|
assert_eq!(lhs_s, rhs_s);
|
||||||
Ok(Value::Bool(lhs < rhs))
|
|
||||||
}
|
match op {
|
||||||
(ast::BinaryOperator::GreaterOrEqual, Value::Int(lhs), Value::Int(rhs)) => {
|
ast::BinaryOperator::Plus => Ok(Value::int(lhs + rhs, lhs_w, lhs_s)),
|
||||||
Ok(Value::Bool(lhs >= rhs))
|
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::NotEquals => {
|
||||||
|
let result = if lhs != rhs { 1 } else { 0 };
|
||||||
|
Ok(Value::int(result, 1, lhs_s))
|
||||||
|
}
|
||||||
|
ast::BinaryOperator::Less => {
|
||||||
|
let result = if lhs < rhs { 1 } else { 0 };
|
||||||
|
Ok(Value::int(result, 1, lhs_s))
|
||||||
|
}
|
||||||
|
ast::BinaryOperator::GreaterOrEqual => {
|
||||||
|
let result = if lhs >= rhs { 1 } else { 0 };
|
||||||
|
Ok(Value::int(result, 1, lhs_s))
|
||||||
|
}
|
||||||
|
_ => todo!("will be covered all operator"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => 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,13 +415,14 @@ 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)
|
|
||||||
};
|
|
||||||
|
|
||||||
self.memory.store(bid, 0, value);
|
if let Some(constant) = initializer {
|
||||||
|
let value = self.interp_constant(constant.clone());
|
||||||
|
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
|
||||||
|
|||||||
@@ -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
2
tests/.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
/platform.info
|
/platform.info
|
||||||
/csmith-*
|
/csmith-*
|
||||||
/test*.c*
|
/test*.c
|
||||||
/reduce-criteria.sh
|
/reduce-criteria.sh
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user