Skip to content

Commit

Permalink
Merge branch 'zheng-ir-interpreter'
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/main/scala/ir/Interpreter.scala
  • Loading branch information
l-kent committed Nov 7, 2023
2 parents e63dcc1 + 7e63dc4 commit ccb7669
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 68 deletions.
46 changes: 32 additions & 14 deletions src/main/scala/ir/Interpreter.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package ir

import analysis.BitVectorEval.*
import util.{LogLevel, Logger}
import util.Logger

import scala.collection.mutable
import scala.util.control.Breaks.{breakable, break}
import scala.util.control.Breaks.{break, breakable}

class Interpreter() {
val regs: mutable.Map[Variable, BitVecLiteral] = mutable.Map()
Expand All @@ -13,7 +13,7 @@ class Interpreter() {
private val FP: BitVecLiteral = BitVecLiteral(4096 - 16, 64)
private val LR: BitVecLiteral = BitVecLiteral(BigInt("FF", 16), 64)
private var nextBlock: Option[Block] = None
private val logLevel: LogLevel = LogLevel.DEBUG
private val returnBlock: mutable.Stack[Block] = mutable.Stack()

def eval(exp: Expr, env: mutable.Map[Variable, BitVecLiteral]): Literal = {
exp match {
Expand Down Expand Up @@ -57,7 +57,9 @@ class Interpreter() {
case bin: BinaryExpr =>
val left: BitVecLiteral = eval(bin.arg1, env).asInstanceOf[BitVecLiteral]
val right: BitVecLiteral = eval(bin.arg2, env).asInstanceOf[BitVecLiteral]
Logger.debug(s"\tBinaryExpr(0x${left.value.toString(16)}[u${left.size}] ${bin.op} 0x${right.value.toString(16)}[u${right.size}])")
Logger.debug(
s"\tBinaryExpr(0x${left.value.toString(16)}[u${left.size}] ${bin.op} 0x${right.value.toString(16)}[u${right.size}])"
)
bin.op match {
case BVAND => smt_bvand(left, right)
case BVOR => smt_bvor(left, right)
Expand Down Expand Up @@ -112,7 +114,8 @@ class Interpreter() {
case ms: MemoryStore =>
val index: Int = eval(ms.index, env).asInstanceOf[BitVecLiteral].value.toInt
val value: BitVecLiteral = eval(ms.value, env).asInstanceOf[BitVecLiteral]
Logger.debug(s"\tMemoryStore(mem:${ms.mem}, index:0x${index.toHexString}, value:0x${value.value.toString(16)}[u${value.size}], size:${ms.size})")
Logger.debug(s"\tMemoryStore(mem:${ms.mem}, index:0x${index.toHexString}, value:0x${value.value
.toString(16)}[u${value.size}], size:${ms.size})")
setMemory(index, ms.size, ms.endian, value, mems)
}
}
Expand All @@ -132,7 +135,13 @@ class Interpreter() {
BitVecLiteral(BigInt(newValue, 2), newSize)
}

def setMemory(index: Int, size: Int, endian: Endian, value: BitVecLiteral, env: mutable.Map[Int, BitVecLiteral]): BitVecLiteral = {
def setMemory(
index: Int,
size: Int,
endian: Endian,
value: BitVecLiteral,
env: mutable.Map[Int, BitVecLiteral]
): BitVecLiteral = {
val binaryString: String = value.value.toString(2).reverse.padTo(size, '0').reverse

val data: List[BitVecLiteral] = endian match {
Expand Down Expand Up @@ -163,7 +172,10 @@ class Interpreter() {
}

// Procedure.Block
nextBlock = Some(p.blocks.head)
p.blocks.headOption match {
case Some(block) => nextBlock = Some(block)
case None => nextBlock = Some(returnBlock.pop())
}
}

private def interpretBlock(b: Block): Unit = {
Expand Down Expand Up @@ -195,12 +207,20 @@ class Interpreter() {
}
case dc: DirectCall =>
Logger.debug(s"$dc")
if (dc.returnTarget.isDefined) {
returnBlock.push(dc.returnTarget.get)
}
interpretProcedure(dc.target)
break
case ic: IndirectCall =>
Logger.debug(s"$ic")
if (ic.target == Register("R30", BitVecType(64)) & ic.returnTarget.isEmpty) {
nextBlock = None
if (ic.target == Register("R30", BitVecType(64)) && ic.returnTarget.isEmpty) {
if (returnBlock.nonEmpty) {
nextBlock = Some(returnBlock.pop())
} else {
//Exit Interpreter
nextBlock = None
}
break
} else {
???
Expand All @@ -226,20 +246,18 @@ class Interpreter() {
Logger.debug(s"MemoryAssign ${assign.lhs} = ${assign.rhs}")
val evalRight = eval(assign.rhs, regs)
evalRight match {
case BitVecLiteral(value, size) => Logger.debug(s"MemoryAssign ${assign.lhs} := 0x${value.toString(16)}[u$size]\n")
case _ => throw new Exception("cannot register non-bitvectors")
case BitVecLiteral(value, size) =>
Logger.debug(s"MemoryAssign ${assign.lhs} := 0x${value.toString(16)}[u$size]\n")
case _ => throw new Exception("cannot register non-bitvectors")
}

//TODO: ASSERT
case assert: Assert =>
Logger.debug(assert)
???
}
}

def interpret(IRProgram: Program): mutable.Map[Variable, BitVecLiteral] = {
Logger.setLevel(logLevel)

// initialize memory array from IRProgram
var currentAddress = 0
IRProgram.initialMemory
Expand Down
174 changes: 120 additions & 54 deletions src/test/scala/ir/InterpreterTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@ package ir
import analysis.BitVectorEval.*
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.BeforeAndAfter
import bap.BAPProgram
import specification.SpecGlobal
import translating.BAPToIR
import util.Logger
import util.{LogLevel, Logger}
import util.RunUtils.{loadBAP, loadReadELF}

import scala.collection.mutable

class InterpreterTests extends AnyFunSuite with BeforeAndAfter {

var i: Interpreter = Interpreter()
Logger.setLevel(LogLevel.DEBUG)

def getProgram(name: String): (Program, Set[SpecGlobal]) = {
val bapProgram = loadBAP(s"examples/$name/$name.adt")
val (externalFunctions, globals, globalOffsets, mainAddress) = loadReadELF(s"examples/$name/$name.relf")
val (externalFunctions, globals, _, mainAddress) = loadReadELF(s"examples/$name/$name.relf")
val IRTranslator = BAPToIR(bapProgram, mainAddress)
var IRProgram = IRTranslator.translate
IRProgram = ExternalRemover(externalFunctions.map(e => e.name)).visitProgram(IRProgram)
Expand All @@ -26,102 +24,170 @@ class InterpreterTests extends AnyFunSuite with BeforeAndAfter {
IRProgram.stackIdentification()
IRProgram.setModifies(Map())


(IRProgram, globals)
}

def runInterpret(name: String): Unit = {
def testInterpret(name: String, expected: Map[String, Int]): Unit = {
val (program, globals) = getProgram(name)
val i = Interpreter()
val regs = i.interpret(program)

// Show interpreted result
Logger.info("Registers:")
regs.foreach { (key, value) =>
Logger.info(s"$key := $value")
}

Logger.info("Globals:")
globals.foreach { global =>
val mem = i.getMemory(global.address.toInt, global.size, Endian.LittleEndian, i.mems)
Logger.info(s"$global := $mem")
}

// Test expected value
expected.foreach { (name, expected) =>
globals.find(_.name == name) match {
case Some(global) =>
val actual = i.getMemory(global.address.toInt, global.size, Endian.LittleEndian, i.mems).value.toInt
assert(actual == expected)
case None => assert("None" == name)
}
}
}

before {
i = Interpreter()
}

test("basic_assign_assign") {
runInterpret("basic_assign_assign")
}

test("basicassign") {
runInterpret("basicassign")
}

test("function") {
runInterpret("function")
}

test("iflocal") {
runInterpret("iflocal")
}

test("ifglobal") {
runInterpret("ifglobal")
}

test("array") {
runInterpret("ifglobal")
}

test("nestedif") {
runInterpret("nestedif")
}

test("nestedifglobal") {
runInterpret("nestedifglobal")
}

test("simple_jump") {
runInterpret("simple_jump")
}

test("getMemory in LittleEndian") {
i.mems(0) = BitVecLiteral(BigInt("0D", 16), 8)
i.mems(1) = BitVecLiteral(BigInt("0C", 16), 8)
i.mems(2) = BitVecLiteral(BigInt("0B", 16), 8)
i.mems(3) = BitVecLiteral(BigInt("0A", 16), 8)
val expect: BitVecLiteral = BitVecLiteral(BigInt("0A0B0C0D", 16), 32)
val expected: BitVecLiteral = BitVecLiteral(BigInt("0A0B0C0D", 16), 32)
val actual: BitVecLiteral = i.getMemory(0, 32, Endian.LittleEndian, i.mems)
assert(actual == expect)
assert(actual == expected)
}

test("getMemory in BigEndian") {
i.mems(0) = BitVecLiteral(BigInt("0A", 16), 8)
i.mems(1) = BitVecLiteral(BigInt("0B", 16), 8)
i.mems(2) = BitVecLiteral(BigInt("0C", 16), 8)
i.mems(3) = BitVecLiteral(BigInt("0D", 16), 8)
val expect: BitVecLiteral = BitVecLiteral(BigInt("0A0B0C0D", 16), 32)
val expected: BitVecLiteral = BitVecLiteral(BigInt("0A0B0C0D", 16), 32)
val actual: BitVecLiteral = i.getMemory(0, 32, Endian.BigEndian, i.mems)
assert(actual == expect)
assert(actual == expected)
}

test("setMemory in LittleEndian") {
i.mems(0) = BitVecLiteral(BigInt("FF", 16), 8)
i.mems(1) = BitVecLiteral(BigInt("FF", 16), 8)
i.mems(2) = BitVecLiteral(BigInt("FF", 16), 8)
i.mems(3) = BitVecLiteral(BigInt("FF", 16), 8)
val expect: BitVecLiteral = BitVecLiteral(BigInt("0A0B0C0D", 16), 32)
i.setMemory(0, 32, Endian.LittleEndian, expect, i.mems)
val expected: BitVecLiteral = BitVecLiteral(BigInt("0A0B0C0D", 16), 32)
i.setMemory(0, 32, Endian.LittleEndian, expected, i.mems)
val actual: BitVecLiteral = i.getMemory(0, 32, Endian.LittleEndian, i.mems)
assert(actual == expect)
assert(actual == expected)
}

test("setMemory in BigEndian") {
i.mems(0) = BitVecLiteral(BigInt("FF", 16), 8)
i.mems(1) = BitVecLiteral(BigInt("FF", 16), 8)
i.mems(2) = BitVecLiteral(BigInt("FF", 16), 8)
i.mems(3) = BitVecLiteral(BigInt("FF", 16), 8)
val expect: BitVecLiteral = BitVecLiteral(BigInt("0A0B0C0D", 16), 32)
i.setMemory(0, 32, Endian.BigEndian, expect, i.mems)
val expected: BitVecLiteral = BitVecLiteral(BigInt("0A0B0C0D", 16), 32)
i.setMemory(0, 32, Endian.BigEndian, expected, i.mems)
val actual: BitVecLiteral = i.getMemory(0, 32, Endian.BigEndian, i.mems)
assert(actual == expect)
assert(actual == expected)
}

test("basic_arrays_read") {
val expected = Map(
"arr" -> 0
)
testInterpret("basic_arrays_read", expected)
}

test("basic_assign_assign") {
val expected = Map(
"x" -> 5
)
testInterpret("basic_assign_assign", expected)
}

test("basic_assign_increment") {
val expected = Map(
"x" -> 1
)
testInterpret("basic_assign_increment", expected)
}

test("basic_loop_loop") {
val expected = Map(
"x" -> 10
)
testInterpret("basic_loop_loop", expected)
}

test("basicassign") {
val expected = Map(
"x" -> 0,
"z" -> 0,
"secret" -> 0
)
testInterpret("basicassign", expected)
}

test("function") {
val expected = Map(
"x" -> 1,
"y" -> 2
)
testInterpret("function", expected)
}

test("function1") {
val expected = Map(
"x" -> 1,
"y" -> 1410065515 // 10000000107 % 2147483648 = 1410065515
)
testInterpret("function1", expected)
}

test("secret_write") {
val expected = Map(
"z" -> 2,
"x" -> 0,
"secret" -> 0
)
testInterpret("secret_write", expected)
}

test("ifglobal") {
val expected = Map(
"x" -> 1
)
testInterpret("ifglobal", expected)
}

test("cjump") {
val expected = Map(
"x" -> 1,
"y" -> 3
)
testInterpret("cjump", expected)
}

test("no_interference_update_x") {
val expected = Map(
"x" -> 1
)
testInterpret("no_interference_update_x", expected)
}

test("no_interference_update_y") {
val expected = Map(
"y" -> 1
)
testInterpret("no_interference_update_y", expected)
}
}

0 comments on commit ccb7669

Please sign in to comment.