-
Notifications
You must be signed in to change notification settings - Fork 220
/
if_complement.rs
132 lines (123 loc) · 4.08 KB
/
if_complement.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! This mutator selects a random `if` construction in a function and swap its branches.
//! Since this mutator preserves the original semantic of the input Wasm,
//! before the mutated if structure is encoded, a "negation" of the previous operand
//! in the stack is written. The "negation" is encoded with a `i32.eqz` operator.
use rand::prelude::SliceRandom;
use wasm_encoder::{Function, Instruction, ValType};
use crate::{
module::map_block_type,
mutators::{
codemotion::{
ir::{parse_context::Ast, AstWriter},
AstMutator,
},
OperatorAndByteOffset,
},
WasmMutate,
};
/// This mutator selects a random `if` construction in a function and swap its branches.
/// Since this mutator preserves the original semantic of the input Wasm,
/// before the mutated if structure is encoded, a "negation" of the previous operand
/// in the stack is written. The "negation" is encoded with a `i32.eqz` operator.
pub struct IfComplementMutator;
#[derive(Default)]
struct IfComplementWriter {
if_to_mutate: usize,
}
impl IfComplementWriter {
/// Swap the then and alternative branches and negates the expected value for the "if" condition
fn write_complement<'a>(
&self,
ast: &Ast,
_: usize,
then: &[usize],
alternative: &Option<Vec<usize>>,
newfunc: &mut wasm_encoder::Function,
operators: &Vec<crate::mutators::OperatorAndByteOffset>,
input_wasm: &'a [u8],
ty: &wasmparser::BlockType,
) -> crate::Result<()> {
// negate the value on the stack
newfunc.instruction(&Instruction::I32Eqz);
newfunc.instruction(&Instruction::If(map_block_type(*ty)?));
// Swap, write alternative first
if let Some(alternative) = alternative {
for ch in alternative {
self.write(ast, *ch, newfunc, operators, input_wasm)?;
}
} else {
// Write an unreachable instruction
newfunc.instruction(&Instruction::Nop);
}
newfunc.instruction(&Instruction::Else);
for ch in then {
self.write(ast, *ch, newfunc, operators, input_wasm)?;
}
newfunc.instruction(&Instruction::End);
Ok(())
}
}
impl AstWriter for IfComplementWriter {
/// Replaces the default implementation of the if_else_writing
fn write_if_else<'a>(
&self,
ast: &Ast,
nodeidx: usize,
then: &[usize],
alternative: &Option<Vec<usize>>,
newfunc: &mut wasm_encoder::Function,
operators: &Vec<crate::mutators::OperatorAndByteOffset>,
input_wasm: &'a [u8],
ty: &wasmparser::BlockType,
) -> crate::Result<()> {
if self.if_to_mutate == nodeidx {
self.write_complement(
ast,
nodeidx,
then,
alternative,
newfunc,
operators,
input_wasm,
ty,
)?;
} else {
self.write_if_else_default(
ast,
nodeidx,
then,
alternative,
newfunc,
operators,
input_wasm,
ty,
)?;
}
Ok(())
}
}
impl AstMutator for IfComplementMutator {
fn can_mutate<'a>(&self, _: &crate::WasmMutate, ast: &Ast) -> bool {
ast.has_if()
}
fn mutate<'a>(
&self,
config: &'a mut WasmMutate,
ast: &Ast,
locals: &[(u32, ValType)],
operators: &Vec<OperatorAndByteOffset>,
input_wasm: &'a [u8],
) -> crate::Result<Function> {
// Select the if index
let mut newfunc = Function::new(locals.to_vec());
let if_index = ast
.get_ifs()
.choose(config.rng())
.expect("This mutator should check first if the AST contains at least one if");
let writer = IfComplementWriter {
if_to_mutate: *if_index,
};
writer.write(ast, ast.get_root(), &mut newfunc, operators, input_wasm)?;
Ok(newfunc)
}
}