Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

br_table support #4

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions data/languages/Webassembly.cspec
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,8 @@
<pcode dynamic="true">
</pcode>
</callotherfixup>
<callotherfixup targetop="brTableCallOther">
<pcode dynamic="true">
</pcode>
</callotherfixup>
</compiler_spec>
1 change: 1 addition & 0 deletions data/languages/Webassembly.sinc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ define pcodeop ifCallOther;
define pcodeop elseCallOther;
define pcodeop returnCallOther;
define pcodeop callIndirectCallOther;
define pcodeop brTableCallOther;

macro push32(val32) {
pushCallOther(32:4);
Expand Down
10 changes: 5 additions & 5 deletions data/languages/br_table.sinc
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ br_table_items: ULeb128^" "^br_table_items is ULeb128; br_table_items [ br_table

:""^indent^"br_table" br_table_items is opc=0x0e; topbit = 0 & v0 ; br_table_items; indent
[ br_table_count = 1 + (0 | v0); ]
unimpl
{ brTableCallOther(); }
:""^indent^"br_table" br_table_items is opc=0x0e; topbit = 1 & v0; topbit = 0 & v1 ; br_table_items; indent
[ br_table_count = 1 + (0 | v0 | ( v1 << 7 )); ]
unimpl
{ brTableCallOther(); }
:""^indent^"br_table" br_table_items is opc=0x0e; topbit = 1 & v0; topbit = 1 & v1; topbit = 0 & v2 ; br_table_items; indent
[ br_table_count = 1 + (0 | v0 | ( v1 << 7 ) | ( v2 << 14 )); ]
unimpl
{ brTableCallOther(); }
:""^indent^"br_table" br_table_items is opc=0x0e; topbit = 1 & v0; topbit = 1 & v1; topbit = 1 & v2; topbit = 0 & v3 ; br_table_items; indent
[ br_table_count = 1 + (0 | v0 | ( v1 << 7 ) | ( v2 << 14 ) | ( v3 << 21 )); ]
unimpl
{ brTableCallOther(); }
:""^indent^"br_table" br_table_items is opc=0x0e; topbit = 1 & v0; topbit = 1 & v1; topbit = 1 & v2; topbit = 1 & v3; topbit = 0 & v4 ; br_table_items; indent
[ br_table_count = 1 + (0 | v0 | ( v1 << 7 ) | ( v2 << 14 ) | ( v3 << 21 ) | ( v4 << 28 )); ]
unimpl
{ brTableCallOther(); }
17 changes: 17 additions & 0 deletions src/main/java/wasm/analysis/BrTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package wasm.analysis;

public class BrTable {
private BrTarget[] cases;

public BrTable(BrTarget[] cases) {
this.cases = cases;
}

public int numCases() {
return cases.length - 1; // default case
}

public BrTarget[] getCases() {
return cases;
}
}
26 changes: 26 additions & 0 deletions src/main/java/wasm/analysis/BrTarget.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package wasm.analysis;

import ghidra.program.model.address.Address;

public class BrTarget {
BranchDest target = null;
int implicitPops;

public BrTarget(BranchDest target, int pops) {
this.implicitPops = pops;
this.target = target;
}

@Override
public String toString() {
return target + " (pops " + implicitPops + ")";
}

public Address getDest() {
return target.getBranchDest();
}

public int getNumPops() {
return implicitPops;
}
}
72 changes: 62 additions & 10 deletions src/main/java/wasm/analysis/MetaInstruction.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public enum Type {
BR,
RETURN,
CALL,
CALL_INDIRECT
CALL_INDIRECT,
BR_TABLE
}

Address location;
Expand Down Expand Up @@ -94,6 +95,10 @@ public static MetaInstruction create(Type ty, InjectContext con, Program p) {
long typeIdx = getLeb128Operand(p, con.baseAddr);
res = new CallIndirectMetaInstruction((int)typeIdx);
break;
case BR_TABLE:
int[] rawCases = readRawBrTable(p, con.baseAddr);
res = new BrTableMetaInstruction(rawCases);
break;
}

if(res != null) {
Expand All @@ -108,12 +113,35 @@ public static MetaInstruction create(Type ty, InjectContext con, Program p) {
return null;
}

private static Leb128 readLeb128(Program p, Address startAddr) throws MemoryAccessException {
byte[] buf = new byte[10];
p.getMemory().getBytes(startAddr, buf);
return Leb128.readUnsignedLeb128(buf);
}

//We have to do this since we cannot resolve non-constant varnode inputs to our CallOther instruction
//But ULeb128 creates a reference varnode in sleigh
public static long getLeb128Operand(Program p, Address brAddress) throws MemoryAccessException {
byte[] buf = new byte[16];
p.getMemory().getBytes(brAddress.add(1), buf); //add 1 to go past the opcode
return Leb128.readUnsignedLeb128(buf);
return readLeb128(p, brAddress.add(1)) //skip the opcode
.getValue();
}

public static int[] readRawBrTable(Program p, Address brAddress) throws MemoryAccessException {
Address nextAddr = brAddress.add(1);
Leb128 numCases = readLeb128(p, nextAddr);
nextAddr = nextAddr.add(numCases.getSize());

int[] res = new int[(int)numCases.getValue() + 1]; //one extra for the default case

for(int i = 0; i < numCases.getValue(); i++) {
Leb128 newCase = readLeb128(p, nextAddr);
nextAddr = nextAddr.add(newCase.getSize());
res[i] = (int)newCase.getValue();
}

res[res.length - 1] = (int)readLeb128(p, nextAddr).getValue(); // the default case

return res;
}

public abstract Type getType();
Expand Down Expand Up @@ -316,8 +344,7 @@ public Type getType() {
}

class BrMetaInstruction extends MetaInstruction {
int implicitPops = 0;
BranchDest target = null;
BrTarget target;
int level;

public BrMetaInstruction(int lvl) {
Expand All @@ -326,16 +353,16 @@ public BrMetaInstruction(int lvl) {

@Override
public String toString() {
return super.toString() + " BR (pops " + implicitPops + ") (dest " + target + ")";
return super.toString() + " BR (dest " + target + ")";
}

@Override
public void synthesize(PcodeOpEmitter pcode) {
if(implicitPops != 0) {
pcode.emitPopn(implicitPops);
if(target.implicitPops != 0) {
pcode.emitPopn(target.implicitPops);
}

pcode.emitJump(target.getBranchDest());
pcode.emitJump(target.getDest());
}

@Override
Expand Down Expand Up @@ -390,4 +417,29 @@ public void synthesize(PcodeOpEmitter pcode) {
public Type getType() {
return Type.CALL_INDIRECT;
}
}

class BrTableMetaInstruction extends MetaInstruction {
int[] rawCases;

BrTable table;

public BrTableMetaInstruction(int[] rawCases) {
this.rawCases = rawCases;
}

@Override
public String toString() {
return super.toString() + " BR_TABLE (dest " + table + ")";
}

@Override
public void synthesize(PcodeOpEmitter pcode) {
pcode.emitBrTable(table);
}

@Override
public Type getType() {
return Type.BR_TABLE;
}
}
60 changes: 42 additions & 18 deletions src/main/java/wasm/analysis/WasmFunctionAnalysis.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,39 @@ public MetaInstruction findMetaInstruction(Address a, MetaInstruction.Type type)
}
return null;
}

private static BrTarget getTarget(int level, ArrayList<MetaInstruction> controlStack, int valueStackDepth) {
MetaInstruction targetInstr = controlStack.get(controlStack.size() - 1 - level);
BranchDest target;
int implicitPops;

switch(targetInstr.getType()) {
case BEGIN_BLOCK:
case IF:
//jump to the end of the corresponding block
target = (BranchDest)targetInstr;
implicitPops = 0;
break;
case BEGIN_LOOP:
//jump back to the beginning of the loop and pop everything that's been pushed since the start
target = (BranchDest)targetInstr;
BeginLoopMetaInstruction loop = (BeginLoopMetaInstruction)target;
implicitPops = valueStackDepth - loop.stackDepthAtStart;
break;
default:
throw new RuntimeException("Invalid item on control stack " + targetInstr);
}

return new BrTarget(target, implicitPops);
}

private static BrTable getBrTable(int[] rawCases, ArrayList<MetaInstruction> controlStack, int valueStackDepth) {
BrTarget[] cases = new BrTarget[rawCases.length];
for(int i = 0; i < rawCases.length; i++) {
cases[i] = getTarget(rawCases[i], controlStack, valueStackDepth);
}
return new BrTable(cases);
}

//Resolve branch targets, implicit pops, call instructions to make them ready for pcode synthesis
public void performResolution() {
Expand All @@ -87,23 +120,7 @@ public void performResolution() {
break;
case BR:
BrMetaInstruction br = (BrMetaInstruction)instr;
MetaInstruction target = controlStack.get(controlStack.size() - 1 - br.level);
switch(target.getType()) {
case BEGIN_BLOCK:
case IF:
//jump to the end of the corresponding block
br.target = (BranchDest)target;
br.implicitPops = 0;
break;
case BEGIN_LOOP:
//jump back to the beginning of the loop and pop everything that's been pushed since the start
br.target = (BranchDest)target;
BeginLoopMetaInstruction loop = (BeginLoopMetaInstruction)target;
br.implicitPops = valueStackDepth - loop.stackDepthAtStart;
break;
default:
throw new RuntimeException("Invalid item on control stack " + target);
}
br.target = getTarget(br.level, controlStack, valueStackDepth);
break;
case ELSE:
IfMetaInstruction ifStmt = (IfMetaInstruction) controlStack.get(controlStack.size() - 1);
Expand Down Expand Up @@ -136,7 +153,8 @@ public void performResolution() {
case RETURN:
if(valueStackDepth != 0) {
if(valueStackDepth != 1) {
throw new RuntimeException("Too many items on stack at return (entry point " + function.getEntryPoint() + ")");
System.out.println(metas);
throw new RuntimeException("Too many items on stack at return (at " + instr.location + ")");
}
ReturnMetaInstruction ret = (ReturnMetaInstruction) instr;
ret.returnsVal = true;
Expand All @@ -159,6 +177,12 @@ public void performResolution() {
valueStackDepth--;
valueStackDepth -= type.getParamTypes().length;
valueStackDepth += type.getReturnTypes().length;
break;
case BR_TABLE:
BrTableMetaInstruction brTableInstr = (BrTableMetaInstruction) instr;
valueStackDepth--;
brTableInstr.table = getBrTable(brTableInstr.rawCases, controlStack, valueStackDepth);
break;
}
}
}
Expand Down
Loading