1use std::vec::Vec;
2
3use swasm::{elements, builder};
4use rules;
5
6pub fn update_call_index(instructions: &mut elements::Instructions, inserted_index: u32) {
7 use swasm::elements::Instruction::*;
8 for instruction in instructions.elements_mut().iter_mut() {
9 if let &mut Call(ref mut call_index) = instruction {
10 if *call_index >= inserted_index { *call_index += 1}
11 }
12 }
13}
14
15#[derive(Debug)]
34struct BlockEntry {
35 start_pos: usize,
37 cost: u32,
39}
40
41struct Counter {
42 blocks: Vec<BlockEntry>,
44
45 stack: Vec<usize>,
47}
48
49impl Counter {
50 fn new() -> Counter {
51 Counter {
52 stack: Vec::new(),
53 blocks: Vec::new(),
54 }
55 }
56
57 fn begin(&mut self, cursor: usize) {
59 let block_idx = self.blocks.len();
60 self.blocks.push(BlockEntry {
61 start_pos: cursor,
62 cost: 1,
63 });
64 self.stack.push(block_idx);
65 }
66
67 fn finalize(&mut self) -> Result<(), ()> {
71 self.stack.pop().ok_or_else(|| ())?;
72 Ok(())
73 }
74
75 fn increment(&mut self, val: u32) -> Result<(), ()> {
77 let stack_top = self.stack.last_mut().ok_or_else(|| ())?;
78 let top_block = self.blocks.get_mut(*stack_top).ok_or_else(|| ())?;
79
80 top_block.cost = top_block.cost.checked_add(val).ok_or_else(|| ())?;
81
82 Ok(())
83 }
84}
85
86fn inject_grow_counter(instructions: &mut elements::Instructions, grow_counter_func: u32) -> usize {
87 use swasm::elements::Instruction::*;
88 let mut counter = 0;
89 for instruction in instructions.elements_mut() {
90 if let GrowMemory(_) = *instruction {
91 *instruction = Call(grow_counter_func);
92 counter += 1;
93 }
94 }
95 counter
96}
97
98fn add_grow_counter(module: elements::Module, rules: &rules::Set, gas_func: u32) -> elements::Module {
99 use swasm::elements::Instruction::*;
100
101 let mut b = builder::from_module(module);
102 b.push_function(
103 builder::function()
104 .signature().params().i32().build().with_return_type(Some(elements::ValueType::I32)).build()
105 .body()
106 .with_instructions(elements::Instructions::new(vec![
107 GetLocal(0),
108 GetLocal(0),
109 I32Const(rules.grow_cost() as i32),
110 I32Mul,
111 Call(gas_func),
113 GrowMemory(0),
114 End,
115 ]))
116 .build()
117 .build()
118 );
119
120 b.build()
121}
122
123pub fn inject_counter(
124 instructions: &mut elements::Instructions,
125 rules: &rules::Set,
126 gas_func: u32,
127) -> Result<(), ()> {
128 use swasm::elements::Instruction::*;
129
130 let mut counter = Counter::new();
131
132 counter.begin(0);
134
135 for cursor in 0..instructions.elements().len() {
136 let instruction = &instructions.elements()[cursor];
137 match *instruction {
138 Block(_) | If(_) | Loop(_) => {
139 let instruction_cost = rules.process(instruction)?;
141 counter.increment(instruction_cost)?;
142
143 counter.begin(cursor + 1);
146 }
147 End => {
148 counter.finalize()?;
150 },
151 Else => {
152 counter.finalize()?;
163 counter.begin(cursor + 1);
164 }
165 _ => {
166 let instruction_cost = rules.process(instruction)?;
168 counter.increment(instruction_cost)?;
169 }
170 }
171 }
172
173 let mut cumulative_offset = 0;
175 for block in counter.blocks {
176 let effective_pos = block.start_pos + cumulative_offset;
177
178 instructions.elements_mut().insert(effective_pos, I32Const(block.cost as i32));
179 instructions.elements_mut().insert(effective_pos+1, Call(gas_func));
180
181 cumulative_offset += 2;
183 }
184
185 Ok(())
186}
187
188pub fn inject_gas_counter(module: elements::Module, rules: &rules::Set)
193 -> Result<elements::Module, elements::Module>
194{
195 let mut mbuilder = builder::from_module(module);
197 let import_sig = mbuilder.push_signature(
198 builder::signature()
199 .param().i32()
200 .build_sig()
201 );
202
203 mbuilder.push_import(
204 builder::import()
205 .module("env")
206 .field("gas")
207 .external().func(import_sig)
208 .build()
209 );
210
211 let mut module = mbuilder.build();
213
214 let gas_func = module.import_count(elements::ImportCountType::Function) as u32 - 1;
218 let total_func = module.functions_space() as u32;
219 let mut need_grow_counter = false;
220 let mut error = false;
221
222 for section in module.sections_mut() {
224 match section {
225 &mut elements::Section::Code(ref mut code_section) => {
226 for ref mut func_body in code_section.bodies_mut() {
227 update_call_index(func_body.code_mut(), gas_func);
228 if let Err(_) = inject_counter(func_body.code_mut(), rules, gas_func) {
229 error = true;
230 break;
231 }
232 if rules.grow_cost() > 0 {
233 if inject_grow_counter(func_body.code_mut(), total_func) > 0 {
234 need_grow_counter = true;
235 }
236 }
237 }
238 },
239 &mut elements::Section::Export(ref mut export_section) => {
240 for ref mut export in export_section.entries_mut() {
241 if let &mut elements::Internal::Function(ref mut func_index) = export.internal_mut() {
242 if *func_index >= gas_func { *func_index += 1}
243 }
244 }
245 },
246 &mut elements::Section::Element(ref mut elements_section) => {
247 for ref mut segment in elements_section.entries_mut() {
248 for func_index in segment.members_mut() {
250 if *func_index >= gas_func { *func_index += 1}
251 }
252 }
253 },
254 &mut elements::Section::Start(ref mut start_idx) => {
255 if *start_idx >= gas_func { *start_idx += 1}
256 },
257 _ => { }
258 }
259 }
260
261 if error { return Err(module); }
262
263 if need_grow_counter { Ok(add_grow_counter(module, rules, gas_func)) } else { Ok(module) }
264}
265
266#[cfg(test)]
267mod tests {
268
269 extern crate wabt;
270
271 use swasm::{serialize, builder, elements};
272 use super::*;
273 use rules;
274
275 #[test]
276 fn simple_grow() {
277 use swasm::elements::Instruction::*;
278
279 let module = builder::module()
280 .global()
281 .value_type().i32()
282 .build()
283 .function()
284 .signature().param().i32().build()
285 .body()
286 .with_instructions(elements::Instructions::new(
287 vec![
288 GetGlobal(0),
289 GrowMemory(0),
290 End
291 ]
292 ))
293 .build()
294 .build()
295 .build();
296
297 let injected_module = inject_gas_counter(module, &rules::Set::default().with_grow_cost(10000)).unwrap();
298
299 assert_eq!(
300 &vec![
301 I32Const(3),
302 Call(0),
303 GetGlobal(0),
304 Call(2),
305 End
306 ][..],
307 injected_module
308 .code_section().expect("function section should exist").bodies()[0]
309 .code().elements()
310 );
311 assert_eq!(
312 &vec![
313 GetLocal(0),
314 GetLocal(0),
315 I32Const(10000),
316 I32Mul,
317 Call(0),
318 GrowMemory(0),
319 End,
320 ][..],
321 injected_module
322 .code_section().expect("function section should exist").bodies()[1]
323 .code().elements()
324 );
325
326 let binary = serialize(injected_module).expect("serialization failed");
327 self::wabt::swasm2wat(&binary).unwrap();
328 }
329
330 #[test]
331 fn grow_no_gas_no_track() {
332 use swasm::elements::Instruction::*;
333
334 let module = builder::module()
335 .global()
336 .value_type().i32()
337 .build()
338 .function()
339 .signature().param().i32().build()
340 .body()
341 .with_instructions(elements::Instructions::new(
342 vec![
343 GetGlobal(0),
344 GrowMemory(0),
345 End
346 ]
347 ))
348 .build()
349 .build()
350 .build();
351
352 let injected_module = inject_gas_counter(module, &rules::Set::default()).unwrap();
353
354 assert_eq!(
355 &vec![
356 I32Const(3),
357 Call(0),
358 GetGlobal(0),
359 GrowMemory(0),
360 End
361 ][..],
362 injected_module
363 .code_section().expect("function section should exist").bodies()[0]
364 .code().elements()
365 );
366
367 assert_eq!(injected_module.functions_space(), 2);
368
369 let binary = serialize(injected_module).expect("serialization failed");
370 self::wabt::swasm2wat(&binary).unwrap();
371 }
372
373 #[test]
374 fn simple() {
375 use swasm::elements::Instruction::*;
376
377 let module = builder::module()
378 .global()
379 .value_type().i32()
380 .build()
381 .function()
382 .signature().param().i32().build()
383 .body()
384 .with_instructions(elements::Instructions::new(
385 vec![
386 GetGlobal(0),
387 End
388 ]
389 ))
390 .build()
391 .build()
392 .build();
393
394 let injected_module = inject_gas_counter(module, &Default::default()).unwrap();
395
396 assert_eq!(
397 &vec![
398 I32Const(2),
399 Call(0),
400 GetGlobal(0),
401 End
402 ][..],
403 injected_module
404 .code_section().expect("function section should exist").bodies()[0]
405 .code().elements()
406 );
407 }
408
409 #[test]
410 fn nested() {
411 use swasm::elements::Instruction::*;
412
413 let module = builder::module()
414 .global()
415 .value_type().i32()
416 .build()
417 .function()
418 .signature().param().i32().build()
419 .body()
420 .with_instructions(elements::Instructions::new(
421 vec![
422 GetGlobal(0),
423 Block(elements::BlockType::NoResult),
424 GetGlobal(0),
425 GetGlobal(0),
426 GetGlobal(0),
427 End,
428 GetGlobal(0),
429 End
430 ]
431 ))
432 .build()
433 .build()
434 .build();
435
436 let injected_module = inject_gas_counter(module, &Default::default()).unwrap();
437
438 assert_eq!(
439 &vec![
440 I32Const(4),
441 Call(0),
442 GetGlobal(0),
443 Block(elements::BlockType::NoResult),
444 I32Const(4),
445 Call(0),
446 GetGlobal(0),
447 GetGlobal(0),
448 GetGlobal(0),
449 End,
450 GetGlobal(0),
451 End
452 ][..],
453 injected_module
454 .code_section().expect("function section should exist").bodies()[0]
455 .code().elements()
456 );
457 }
458
459 #[test]
460 fn ifelse() {
461 use swasm::elements::Instruction::*;
462
463 let module = builder::module()
464 .global()
465 .value_type().i32()
466 .build()
467 .function()
468 .signature().param().i32().build()
469 .body()
470 .with_instructions(elements::Instructions::new(
471 vec![
472 GetGlobal(0),
473 If(elements::BlockType::NoResult),
474 GetGlobal(0),
475 GetGlobal(0),
476 GetGlobal(0),
477 Else,
478 GetGlobal(0),
479 GetGlobal(0),
480 End,
481 GetGlobal(0),
482 End
483 ]
484 ))
485 .build()
486 .build()
487 .build();
488
489 let injected_module = inject_gas_counter(module, &Default::default()).unwrap();
490
491 assert_eq!(
492 &vec![
493 I32Const(4),
494 Call(0),
495 GetGlobal(0),
496 If(elements::BlockType::NoResult),
497 I32Const(4),
498 Call(0),
499 GetGlobal(0),
500 GetGlobal(0),
501 GetGlobal(0),
502 Else,
503 I32Const(3),
504 Call(0),
505 GetGlobal(0),
506 GetGlobal(0),
507 End,
508 GetGlobal(0),
509 End
510 ][..],
511 injected_module
512 .code_section().expect("function section should exist").bodies()[0]
513 .code().elements()
514 );
515 }
516
517 #[test]
518 fn call_index() {
519 use swasm::elements::Instruction::*;
520
521 let module = builder::module()
522 .global()
523 .value_type().i32()
524 .build()
525 .function()
526 .signature().param().i32().build()
527 .body().build()
528 .build()
529 .function()
530 .signature().param().i32().build()
531 .body()
532 .with_instructions(elements::Instructions::new(
533 vec![
534 Call(0),
535 If(elements::BlockType::NoResult),
536 Call(0),
537 Call(0),
538 Call(0),
539 Else,
540 Call(0),
541 Call(0),
542 End,
543 Call(0),
544 End
545 ]
546 ))
547 .build()
548 .build()
549 .build();
550
551 let injected_module = inject_gas_counter(module, &Default::default()).unwrap();
552
553 assert_eq!(
554 &vec![
555 I32Const(4),
556 Call(0),
557 Call(1),
558 If(elements::BlockType::NoResult),
559 I32Const(4),
560 Call(0),
561 Call(1),
562 Call(1),
563 Call(1),
564 Else,
565 I32Const(3),
566 Call(0),
567 Call(1),
568 Call(1),
569 End,
570 Call(1),
571 End
572 ][..],
573 injected_module
574 .code_section().expect("function section should exist").bodies()[1]
575 .code().elements()
576 );
577 }
578
579
580 #[test]
581 fn forbidden() {
582 use swasm::elements::Instruction::*;
583
584 let module = builder::module()
585 .global()
586 .value_type().i32()
587 .build()
588 .function()
589 .signature().param().i32().build()
590 .body()
591 .with_instructions(elements::Instructions::new(
592 vec![
593 F32Const(555555),
594 End
595 ]
596 ))
597 .build()
598 .build()
599 .build();
600
601 let rules = rules::Set::default().with_forbidden_floats();
602
603 if let Err(_) = inject_gas_counter(module, &rules) { }
604 else { panic!("Should be error because of the forbidden operation")}
605
606 }
607
608}