Skip to content

Commit 322a163

Browse files
cdetrioaxic
authored andcommitted
off-by-one from unmetered instruction count for ecmul
1 parent 7ea0722 commit 322a163

File tree

2 files changed

+85
-43
lines changed

2 files changed

+85
-43
lines changed

wasm-utils/cli/gas/main.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ fn main() {
1515

1616
let memory_page_cost = 256 * 1024; // 256k gas for 1 page (64k) of memory
1717

18+
// let config = pwasm_utils::rules::Set::default()
19+
// .with_forbidden_floats() // Reject floating point opreations.
20+
// .with_grow_cost(memory_page_cost);
21+
1822
let config = pwasm_utils::rules::Set::default()
19-
.with_forbidden_floats() // Reject floating point opreations.
20-
.with_grow_cost(memory_page_cost);
23+
.with_forbidden_floats();
2124

2225
// Loading module
2326
let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed");

wasm-utils/src/gas.rs

Lines changed: 80 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ struct BlockEntry {
3636
start_pos: usize,
3737
/// Sum of costs of all instructions until end of the block.
3838
cost: u32,
39+
flow_up: bool,
3940
}
4041

4142
struct Counter {
@@ -55,11 +56,13 @@ impl Counter {
5556
}
5657

5758
/// Begin a new block.
58-
fn begin(&mut self, cursor: usize, cost: u32) {
59+
fn begin(&mut self, cursor: usize, cost: u32, flow_up: bool) {
5960
let block_idx = self.blocks.len();
61+
println!("beginning new block at idx: {:?}", block_idx);
6062
self.blocks.push(BlockEntry {
6163
start_pos: cursor,
6264
cost: cost,
65+
flow_up: flow_up,
6366
});
6467
self.stack.push(block_idx);
6568
}
@@ -72,7 +75,7 @@ impl Counter {
7275
println!("stack size after finalizing: {:?}", self.stack.len());
7376
Ok(())
7477
}
75-
78+
7679
fn increment_control_flow(&mut self, val: u32) -> Result<(), ()> {
7780
/*
7881
;; if the current block and parent block has 0 cost, then we're seeing a sequence of nested blocks
@@ -105,29 +108,20 @@ impl Counter {
105108
)))
106109
*/
107110

108-
let stack_top = self.stack.last_mut().ok_or_else(|| ())?;
109-
let top_block = self.blocks.get_mut(*stack_top).ok_or_else(|| ())?;
111+
// find closest ancestor block (starting from top of stack and going down) with blocked flow and add 1
110112

111-
if top_block.cost > 0 || *stack_top == 0 {
112-
// if current block already has cost (i.e. previous instructions), or if there's no parent block,
113-
// then increment gas in this block
114-
println!("current block already has instructions, or no parent block. incrementing and returning...");
115-
top_block.cost = top_block.cost.checked_add(val).ok_or_else(|| ())?;
116-
Ok(())
117-
} else {
118-
// find closest ancestor block (starting from top of stack and going down) with useGas call and add 1
119-
for (i, stack_i) in self.stack.iter().rev().enumerate() {
120-
println!("stack at position {}: {:?}", i, stack_i);
121-
let block_i = self.blocks.get_mut(*stack_i).ok_or_else(|| ())?;
122-
println!("block_i has cost: {:?}", block_i.cost);
123-
if *stack_i == 0 || block_i.cost > 0 {
124-
println!("found ancestor with cost > 0 or root block. incrementing and returning...");
125-
block_i.cost = block_i.cost.checked_add(val).ok_or_else(|| ())?;
126-
break;
127-
}
113+
for (i, stack_i) in self.stack.iter().rev().enumerate() {
114+
println!("stack at position {}: {:?}", i, stack_i);
115+
let block_i = self.blocks.get_mut(*stack_i).ok_or_else(|| ())?;
116+
println!("block_{:?} has cost: {:?}", *stack_i, block_i.cost);
117+
if !block_i.flow_up || *stack_i == 0 {
118+
block_i.cost = block_i.cost.checked_add(val).ok_or_else(|| ())?;
119+
println!("found ancestor with blocked flow or no parent. incrementing to new cost: {:?} and returning...", block_i.cost);
120+
break;
128121
}
129-
Ok(())
130122
}
123+
Ok(())
124+
131125
}
132126

133127
/// Increment the cost of the current block by the specified value.
@@ -136,6 +130,7 @@ impl Counter {
136130
let top_block = self.blocks.get_mut(*stack_top).ok_or_else(|| ())?;
137131

138132
top_block.cost = top_block.cost.checked_add(val).ok_or_else(|| ())?;
133+
println!("instruction for current block. incrementing, new cost: {:?}", top_block.cost);
139134

140135
Ok(())
141136
}
@@ -164,8 +159,10 @@ fn add_grow_counter(module: elements::Module, rules: &rules::Set, gas_func: u32)
164159
.with_instructions(elements::Instructions::new(vec![
165160
GetLocal(0),
166161
GetLocal(0),
167-
I64Const(rules.grow_cost() as i64),
168-
I64Mul,
162+
// I64Const(rules.grow_cost() as i64),
163+
// I64Mul,
164+
I32Const(rules.grow_cost() as i32),
165+
I32Mul,
169166
// todo: there should be strong guarantee that it does not return anything on stack?
170167
Call(gas_func),
171168
GrowMemory(0),
@@ -188,7 +185,7 @@ pub fn inject_counter(
188185
let mut counter = Counter::new();
189186

190187
// Begin an implicit function (i.e. `func...end`) block.
191-
counter.begin(0, 0);
188+
counter.begin(0, 0, false);
192189

193190
for cursor in 0..instructions.elements().len() {
194191
let instruction = &instructions.elements()[cursor];
@@ -197,36 +194,71 @@ pub fn inject_counter(
197194
// Increment previous block with the cost of the current opcode.
198195
let instruction_cost = rules.process(instruction)?;
199196
//counter.increment(instruction_cost)?;
200-
counter.increment_control_flow(instruction_cost)?;
201197

202198
// Begin new block. The cost of the following opcodes until `End` or `Else` will
203199
// be included into this block.
200+
201+
// add cost, which may flow up to ancestor block
202+
counter.increment_control_flow(instruction_cost)?;
203+
204+
counter.begin(cursor + 1, 0, true);
204205

205-
// begin blocks with cost 0
206-
counter.begin(cursor + 1, 0);
207206
},
208207
If(_) => {
208+
println!("on if instruction. finalizing current block and beginning new...");
209209
// Increment previous block with the cost of the current opcode.
210210
let instruction_cost = rules.process(instruction)?;
211211
//counter.increment(instruction_cost)?;
212212
counter.increment_control_flow(instruction_cost)?;
213-
214-
// begin If with cost 1.
215-
counter.begin(cursor + 1, 1);
213+
214+
// begin If with cost 1, to force new costs added to top of block
215+
counter.begin(cursor + 1, 0, false);
216216
},
217-
Loop(_) => {
217+
BrIf(_) => {
218+
println!("on br_if instruction. finalizing current block and beginning new...");
218219
// Increment previous block with the cost of the current opcode.
219220
let instruction_cost = rules.process(instruction)?;
220221
//counter.increment(instruction_cost)?;
221222
counter.increment_control_flow(instruction_cost)?;
222-
223-
// begin loop with cost 1.
224-
counter.begin(cursor + 1, 1);
223+
224+
// on a br_if, we finalize the previous block because those instructions will always be executed.
225+
// intructions after the if will be executed conditionally, so we start a new block so that gasUsed can be called after the if.
226+
counter.finalize()?;
227+
228+
// begin If with cost 1, to force new costs added to top of block
229+
counter.begin(cursor + 1, 0, false);
225230
},
231+
Loop(_) => {
232+
let instruction_cost = rules.process(instruction)?;
233+
//counter.increment(instruction_cost)?;
234+
//counter.increment_control_flow(instruction_cost)?;
235+
236+
counter.begin(cursor + 1, 0, false);
237+
// charge for the loop after the loop instruction
238+
// need to do this because the loop could be executed many times (the br_if that jumps to the loop is a separate instruction and gas charge)
239+
counter.increment_control_flow(instruction_cost)?;
240+
},
241+
Br(_) => {
242+
// anything after a break is dead code.
243+
// for now, we treat dead code blocks like any other (the metering will not be executed)
244+
// TODO: handle properly and don't inject metering inside dead code blocks
245+
let instruction_cost = rules.process(instruction)?;
246+
counter.increment_control_flow(instruction_cost)?;
247+
counter.finalize()?;
248+
counter.begin(cursor + 1, 0, false);
249+
},
250+
// br_table is always followed by end (in the ecmul wasm code, at least)
251+
// BrTable(_,_) => { },
252+
// return is always followed by end (in the ecmul wasm code, at least)
253+
// Return => { },
226254
End => {
227255
// Just finalize current block.
256+
//counter.increment_control_flow(instruction_cost)?;
257+
// wasabi doesn't count end as an instruction, so neither will we (no gas charge)
258+
259+
228260
counter.finalize()?;
229-
counter.begin(cursor + 1, 0);
261+
counter.begin(cursor + 1, 0, false);
230262
},
231263
Else => {
232264
// `Else` opcode is being encountered. So the case we are looking at:
@@ -240,12 +272,17 @@ pub fn inject_counter(
240272
// Finalize the current block ('then' part of the if statement),
241273
// and begin another one for the 'else' part.
242274
counter.finalize()?;
243-
counter.begin(cursor + 1, 1);
244-
}
275+
counter.begin(cursor + 1, 1, false);
276+
},
277+
Unreachable => {
278+
// charge nothing, do nothing
279+
println!("skipping unreachable...");
280+
},
245281
_ => {
246282
// An ordinal non control flow instruction. Just increment the cost of the current block.
247283
let instruction_cost = rules.process(instruction)?;
248-
counter.increment(instruction_cost)?;
284+
//counter.increment(instruction_cost)?;
285+
counter.increment_control_flow(instruction_cost)?;
249286
}
250287
}
251288
}
@@ -256,7 +293,8 @@ pub fn inject_counter(
256293
if block.cost > 0 {
257294
let effective_pos = block.start_pos + cumulative_offset;
258295

259-
instructions.elements_mut().insert(effective_pos, I64Const(block.cost as i64));
296+
//instructions.elements_mut().insert(effective_pos, I64Const(block.cost as i64));
297+
instructions.elements_mut().insert(effective_pos, I32Const(block.cost as i32));
260298
instructions.elements_mut().insert(effective_pos+1, Call(gas_func));
261299

262300
// Take into account these two inserted instructions.
@@ -278,7 +316,7 @@ pub fn inject_gas_counter(module: elements::Module, rules: &rules::Set)
278316
let mut mbuilder = builder::from_module(module);
279317
let import_sig = mbuilder.push_signature(
280318
builder::signature()
281-
.param().i64()
319+
.param().i32()
282320
.build_sig()
283321
);
284322

@@ -306,6 +344,7 @@ pub fn inject_gas_counter(module: elements::Module, rules: &rules::Set)
306344
match section {
307345
&mut elements::Section::Code(ref mut code_section) => {
308346
for ref mut func_body in code_section.bodies_mut() {
347+
println!("doing metering over new function...");
309348
update_call_index(func_body.code_mut(), gas_func);
310349
if let Err(_) = inject_counter(func_body.code_mut(), rules, gas_func) {
311350
error = true;

0 commit comments

Comments
 (0)