danleh / wasabi

A dynamic analysis framework for WebAssembly programs.
http://wasabi.software-lab.org
MIT License
366 stars 48 forks source link

Wasabi.resolveTableIdx implementation needs fixing #21

Closed rxEckT closed 2 years ago

rxEckT commented 4 years ago

Hi Daniel,

When using the same program from #20, I realised that Wasabi returned incorrect answers for indirect function calls.

In [0]: Wasabi.resolveTableIdx(45)
Out[0]: 0 // should be 2
In [1]: instance.exports.__wasabi_table.get(45)
Out[1]: function 33()

The code in question is this: https://github.com/danleh/wasabi/blob/af37e2e0f6f1221a2b40500bf2372b8e7f87680f/lib/runtime.js#L46


If we look at what happens when we do wasm-objdump -x for an instrumented binary we will get this:

brainfuck.test.instrumented.wasm:       file format wasm 0x1

Section Details:

Type[6]:
 - type[0] (i32, i32) -> nil
 - type[1] (i32, i32, i32, i32) -> nil
 - type[2] (i32, i32, i32, i32, i32, i32) -> nil
 - type[3] (i32, i32, i32) -> nil
 - type[4] (i32, i32, i32, i32, i32) -> nil
 - type[5] () -> nil
Import[32]:
 - func[0] sig=0 <a> <- a
 - func[1] sig=0 <__wasabi_hooks.begin_function> <- __wasabi_hooks.begin_function
 - func[2] sig=1 <__wasabi_hooks.global.get_i> <- __wasabi_hooks.global.get_i
 - func[3] sig=2 <__wasabi_hooks.i32.load8_u> <- __wasabi_hooks.i32.load8_u
 - func[4] sig=3 <__wasabi_hooks.i32.const> <- __wasabi_hooks.i32.const
 - func[5] sig=4 <__wasabi_hooks.i32.add> <- __wasabi_hooks.i32.add
 - func[6] sig=2 <__wasabi_hooks.i32.store8> <- __wasabi_hooks.i32.store8
 - func[7] sig=0 <__wasabi_hooks.return> <- __wasabi_hooks.return
 - func[8] sig=0 <__wasabi_hooks.end_function> <- __wasabi_hooks.end_function
 - func[9] sig=4 <__wasabi_hooks.i32.sub> <- __wasabi_hooks.i32.sub
 - func[10] sig=1 <__wasabi_hooks.global.set_i> <- __wasabi_hooks.global.set_i
 - func[11] sig=1 <__wasabi_hooks.i32.eqz> <- __wasabi_hooks.i32.eqz
 - func[12] sig=3 <__wasabi_hooks.if> <- __wasabi_hooks.if
 - func[13] sig=0 <__wasabi_hooks.begin_if> <- __wasabi_hooks.begin_if
 - func[14] sig=3 <__wasabi_hooks.call> <- __wasabi_hooks.call
 - func[15] sig=0 <__wasabi_hooks.call_post> <- __wasabi_hooks.call_post
 - func[16] sig=3 <__wasabi_hooks.end_if> <- __wasabi_hooks.end_if
 - func[17] sig=3 <__wasabi_hooks.begin_else> <- __wasabi_hooks.begin_else
 - func[18] sig=1 <__wasabi_hooks.end_else> <- __wasabi_hooks.end_else
 - func[19] sig=1 <__wasabi_hooks.local.set_i> <- __wasabi_hooks.local.set_i
 - func[20] sig=0 <__wasabi_hooks.begin_loop> <- __wasabi_hooks.begin_loop
 - func[21] sig=1 <__wasabi_hooks.local.get_i> <- __wasabi_hooks.local.get_i
 - func[22] sig=1 <__wasabi_hooks.local.tee_i> <- __wasabi_hooks.local.tee_i
 - func[23] sig=4 <__wasabi_hooks.i32.ne> <- __wasabi_hooks.i32.ne
 - func[24] sig=4 <__wasabi_hooks.br_if> <- __wasabi_hooks.br_if
 - func[25] sig=3 <__wasabi_hooks.end_loop> <- __wasabi_hooks.end_loop
 - func[26] sig=3 <__wasabi_hooks.call_indirect> <- __wasabi_hooks.call_indirect
 - func[27] sig=4 <__wasabi_hooks.i32.gt_s> <- __wasabi_hooks.i32.gt_s
 - func[28] sig=4 <__wasabi_hooks.i32.xor> <- __wasabi_hooks.i32.xor
 - func[29] sig=4 <__wasabi_hooks.i32.gt_u> <- __wasabi_hooks.i32.gt_u
 - func[30] sig=2 <__wasabi_hooks.call_iii> <- __wasabi_hooks.call_iii
 - func[31] sig=4 <__wasabi_hooks.call_ii> <- __wasabi_hooks.call_ii
Function[12]:
 - func[32] sig=5 <inc>
 - func[33] sig=5 <dec>
 - func[34] sig=5 <shl>
 - func[35] sig=5 <shr>
 - func[36] sig=5 <start>
 - func[37] sig=5 <end>
 - func[38] sig=5 <prn>
 - func[39] sig=5
 - func[40] sig=5
 - func[41] sig=5
 - func[42] sig=3
 - func[43] sig=5 <b>
Table[1]:
 - table[0] type=funcref initial=94
Memory[1]:
 - memory[0] pages: initial=1
Global[5]:
 - global[0] i32 mutable=1 - init i32=0
 - global[1] i32 mutable=1 - init i32=0
 - global[2] i32 mutable=1 - init i32=0
 - global[3] i32 mutable=1 - init i32=0
 - global[4] i32 mutable=1 - init i32=1
Export[10]:
 - func[32] <inc> -> "inc"
 - func[33] <dec> -> "dec"
 - func[34] <shl> -> "shl"
 - func[35] <shr> -> "shr"
 - func[36] <start> -> "start"
 - func[37] <end> -> "end"
 - func[38] <prn> -> "prn"
 - func[43] <b> -> "b"
 - table[0] -> "__wasabi_table"
 - memory[0] -> "c"
Elem[7]:
 - segment[0] table=0 count=1 - init i32=43
  - elem[43] = func[32] <inc>
 - segment[1] table=0 count=1 - init i32=45
  - elem[45] = func[33] <dec>
 - segment[2] table=0 count=1 - init i32=60
  - elem[60] = func[34] <shl>
 - segment[3] table=0 count=1 - init i32=62
  - elem[62] = func[35] <shr>
 - segment[4] table=0 count=1 - init i32=91
  - elem[91] = func[36] <start>
 - segment[5] table=0 count=1 - init i32=93
  - elem[93] = func[37] <end>
 - segment[6] table=0 count=1 - init i32=46
  - elem[46] = func[38] <prn>
Code[12]:
 - func[32] size=125 <inc>
 - func[33] size=125 <dec>
 - func[34] size=77 <shl>
 - func[35] size=77 <shr>
 - func[36] size=158 <start>
 - func[37] size=134 <end>
 - func[38] size=149 <prn>
 - func[39] size=225
 - func[40] size=44
 - func[41] size=246
 - func[42] size=380
 - func[43] size=150 <b>

In [2] is 33 because of the imported hooks inflating the function ids.

Therefore, my first thought was subtracting the hook count like so:

const functionIdx = parseInt(Wasabi.module.table.get(tableIdx).name) - Object.entries(Wasabi.module.lowlevelHooks).length;

However, the approach would not work properly if there are existing imports.

I think the best solution would be for Wasabi to emit a importCount in Wasabi.module.info to be used together with the code above.

var functionIdx = parseInt(Wasabi.module.table.get(tableIdx).name);
if (functionIdx > Wasabi.module.info.importCount - 1) {
    functionIdx = functionIdx - Object.entries(Wasabi.module.lowlevelHooks).length;
}
return functionIdx;

Merry Christmas and a Happy New Year! :tada:

danleh commented 2 years ago

Sorry for coming back to this so late. I finally ended up fixing the issue, essentially as you described. Thanks again!