Skip to content

Commit

Permalink
BASIL-19: infinite loop detection
Browse files Browse the repository at this point in the history
  • Loading branch information
ziggyfish committed Oct 5, 2023
1 parent 14e0fd3 commit 92477e3
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 30 deletions.
125 changes: 125 additions & 0 deletions src/main/scala/analysis/NonReturningFunctions.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package analysis

import bap.{BAPJump, BAPSubroutine}
import ir.{Block, DirectCall, GoTo, IndirectCall, Jump, Procedure, Statement}

import scala.collection.mutable.{Map, Queue}
import collection.parallel.CollectionConverters.seqIsParallelizable
import scala.collection.mutable.ArrayBuffer
import scala.collection.parallel.CollectionConverters.*

class NonReturningFunctions {

def transform(procedures: ArrayBuffer[Procedure]): Unit = {
val blocksToRemove: Queue[String] = Queue()
val mapJumpsToBlocks: Map[String, ArrayBuffer[(Jump, Block)]] = Map()
val mapBlocksToProcedure: Map[String, (Procedure, Integer)] = Map()

def isEndlessLoop(proc: Procedure, goTo: GoTo, index: Integer): Boolean = {

if (goTo.condition.isEmpty && mapBlocksToProcedure.contains(goTo.target.label) && mapBlocksToProcedure(goTo.target.label)._2 < index) {

val (_, idx) = mapBlocksToProcedure(goTo.target.label)

for (loopIndex <- idx.toInt to index.toInt) {
val blockAtLoopIndex = proc.blocks(loopIndex)
for (loopJump <- blockAtLoopIndex.jumps) {
loopJump match {
case loopGoTo: GoTo =>
if (!mapBlocksToProcedure.contains(loopGoTo.target.label) || mapBlocksToProcedure(loopGoTo.target.label)._2 >= index)
return false
case call: IndirectCall =>
if (call.target.name == "R30") {
return false
}
case _ =>
}
}

}
return true
}
false
}

for (proc <- procedures) {
var numberOfReturns = 0
for ((block, index) <- proc.blocks.zipWithIndex) {
mapBlocksToProcedure.addOne(block.label, (proc, index))
for (jump <- block.jumps) {

jump match {
case call: IndirectCall =>
if (call.target.name == "R30") {
block.countOfReturnStatements += 1
numberOfReturns += 1
}
case directCall: DirectCall =>
mapJumpsToBlocks.put(directCall.target.name, mapJumpsToBlocks.getOrElse(directCall.target.name, ArrayBuffer()).addOne((directCall, block)))
case goTo: GoTo =>
mapJumpsToBlocks.put(goTo.target.label, mapJumpsToBlocks.getOrElse(goTo.target.label, ArrayBuffer()).addOne((goTo, block)))
if (isEndlessLoop(proc, goTo, index)) {

}
case _ =>
}

}
}
}
var blocksDeleted = true;

while (blocksDeleted) {
blocksDeleted = false
for (proc <- procedures) {

if (proc.calculateReturnCount() == 0) {
mapJumpsToBlocks.get(proc.name) match {
case Some(v) => for (block <- v) {
val (_, containingBlock) = block
for (jump <- containingBlock.jumps) {
jump match {
case directCall: DirectCall =>
directCall.returnTarget match {
case Some(t) =>
blocksToRemove.enqueue(t.label)
case _ =>
}
directCall.returnTarget = None
case _ =>
}
}
}
case _ =>
}
}
}

while (blocksToRemove.nonEmpty) {
val label = blocksToRemove.dequeue()
val (procedure, _) = mapBlocksToProcedure(label)
if (!mapJumpsToBlocks.contains(label) || mapJumpsToBlocks(label).length <= 1) {

var procedureBlock: Integer = null
for ((block, index) <- procedure.blocks.zipWithIndex) {
if (block.label == label) {
procedureBlock = index
for (jump <- block.jumps) {
jump match {
case goTo: GoTo =>
blocksToRemove.enqueue(goTo.target.label)
case _ =>
}
}
}
}
if (procedureBlock != null) {
procedure.blocks.remove(procedureBlock)
blocksDeleted = true
}
}
}
}

}
}
11 changes: 7 additions & 4 deletions src/main/scala/ir/Program.scala
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,18 @@ class Procedure(
var address: Option[Int],
var blocks: ArrayBuffer[Block],
var in: ArrayBuffer[Parameter],
var out: ArrayBuffer[Parameter],
var nonReturning: Boolean
var out: ArrayBuffer[Parameter]
) {

def calls: Set[Procedure] = blocks.flatMap(_.calls).toSet
override def toString: String = {
s"Procedure $name at ${address.getOrElse("None")} with ${blocks.size} blocks and ${in.size} in and ${out.size} out parameters" + (if (nonReturning) " Non-Returning" else "")
s"Procedure $name at ${address.getOrElse("None")} with ${blocks.size} blocks and ${in.size} in and ${out.size} out parameters")
}
var modifies: mutable.Set[Global] = mutable.Set()

def calculateReturnCount(): Int = {
return blocks.foldLeft(0)(_ + _.countOfReturnStatements)
}
def stackIdentification(): Unit = {
val stackPointer = Register("R31", BitVecType(64))
val stackRefs: mutable.Set[Variable] = mutable.Set(stackPointer)
Expand Down Expand Up @@ -174,7 +176,8 @@ class Block(
var label: String,
var address: Option[Int],
var statements: ArrayBuffer[Statement],
var jumps: ArrayBuffer[Jump]
var jumps: ArrayBuffer[Jump],
var countOfReturnStatements: Int
) {
def calls: Set[Procedure] = jumps.flatMap(_.calls).toSet
def modifies: Set[Global] = statements.flatMap(_.modifies).toSet
Expand Down
36 changes: 11 additions & 25 deletions src/main/scala/translating/BAPToIR.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package translating

import bap._
import ir._
import specification._
import analysis.NonReturningFunctions
import bap.*
import ir.*
import specification.*

import scala.collection.mutable
import collection.parallel.CollectionConverters.seqIsParallelizable
import scala.collection.parallel.CollectionConverters._
import scala.collection.mutable.Map
import scala.collection.mutable.ArrayBuffer

Expand All @@ -20,7 +20,7 @@ class BAPToIR(var program: BAPProgram, mainAddress: Int) {
for (s <- program.subroutines) {
val blocks: ArrayBuffer[Block] = ArrayBuffer()
for (b <- s.blocks) {
val block = Block(b.label, b.address, ArrayBuffer(), ArrayBuffer())
val block = Block(b.label, b.address, ArrayBuffer(), ArrayBuffer(), 0)
blocks.append(block)
labelToBlock.addOne(b.label, block)
}
Expand All @@ -32,38 +32,27 @@ class BAPToIR(var program: BAPProgram, mainAddress: Int) {
for (p <- s.out) {
out.append(p.toIR)
}
val procedure = Procedure(s.name, Some(s.address), blocks, in, out, BAPLoader.isNonReturning(s.name))
val procedure = Procedure(s.name, Some(s.address), blocks, in, out)
if (s.address == mainAddress) {
mainProcedure = Some(procedure)
}
procedures.append(procedure)
nameToProcedure.addOne(s.name, procedure)
}

for (s <- program.subroutines.par) {

var isReturning = false
for (s <- program.subroutines) {
for (b <- s.blocks) {
val block = labelToBlock(b.label)

for (st <- b.statements) {
block.statements.append(translate(st))
}

for (j <- b.jumps) {
val translated = translate(j)
if (translated.isInstanceOf[DirectCall] && translated.asInstanceOf[DirectCall].target.nonReturning) {
translated.asInstanceOf[DirectCall].returnTarget = None
}
if (j.isInstanceOf[BAPIndirectCall] && j.asInstanceOf[BAPIndirectCall].target.name == "R30") {
isReturning = true
}

block.jumps.append(translated)

}
}
if (!isReturning) {
nameToProcedure(s.name).nonReturning = true
}
}

val memorySections: ArrayBuffer[MemorySection] = ArrayBuffer()
Expand All @@ -87,10 +76,7 @@ class BAPToIR(var program: BAPProgram, mainAddress: Int) {
DirectCall(
nameToProcedure(b.target),
coerceToBool(b.condition),
if (nameToProcedure(b.target).nonReturning)
Option.empty
else
b.returnTarget.map { (t: String) => labelToBlock(t) }
b.returnTarget.map { (t: String) => labelToBlock(t) }
)
case b: BAPIndirectCall =>
IndirectCall(b.target.toIR, coerceToBool(b.condition), b.returnTarget.map { (t: String) => labelToBlock(t) })
Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/util/RunUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ object RunUtils {

val IRTranslator = BAPToIR(bapProgram, mainAddress)
var IRProgram = IRTranslator.translate
NonReturningFunctions().transform(IRProgram.procedures)

val specification = loadSpecification(specFileName, IRProgram, globals)

Expand Down Expand Up @@ -315,7 +316,7 @@ object RunUtils {
}

def addFakeProcedure(name: String): Unit = {
IRProgram.procedures += Procedure(name, None, ArrayBuffer(), ArrayBuffer(), ArrayBuffer(), true)
IRProgram.procedures += Procedure(name, None, ArrayBuffer(), ArrayBuffer(), ArrayBuffer())
}

def resolveAddresses(valueSet: Set[Value]): Set[AddressValue] = {
Expand Down

0 comments on commit 92477e3

Please sign in to comment.