Skip to content

Commit

Permalink
feature/labelInLoop
Browse files Browse the repository at this point in the history
  • Loading branch information
Yi2255 committed Dec 7, 2024
1 parent d4796ed commit facb0f9
Show file tree
Hide file tree
Showing 14 changed files with 563 additions and 1 deletion.
8 changes: 8 additions & 0 deletions Sources/Fuzzilli/Base/ProgramBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2531,6 +2531,14 @@ public class ProgramBuilder {
emit(LoopContinue(), withInputs: [])
}

public func breakNested(_ depth: Variable){
emit(BreakNested(), withInputs: [depth])
}

public func continueNested(_ depth: Variable){
emit(ContinueNested(), withInputs: [depth])
}

public func buildTryCatchFinally(tryBody: () -> (), catchBody: ((Variable) -> ())? = nil, finallyBody: (() -> ())? = nil) {
assert(catchBody != nil || finallyBody != nil, "Must have either a Catch or a Finally block (or both)")
emit(BeginTry())
Expand Down
2 changes: 2 additions & 0 deletions Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,6 @@ public let codeGeneratorWeights = [
"ApiMethodCallGenerator": 15,
"ApiFunctionCallGenerator": 15,
"VoidGenerator": 1,
"LoopLabelBreakGenerator": 3,
"ContinueLabelGenerator": 3
]
9 changes: 9 additions & 0 deletions Sources/Fuzzilli/CodeGen/CodeGenerators.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,15 @@ public let CodeGenerators: [CodeGenerator] = [
b.loopContinue()
},

CodeGenerator("LoopLabelBreakGenerator", inContext: .loop) { b in
b.breakNested(b.loadInt(Int64.random(in: 0...100)))
},

CodeGenerator("ContinueLabelGenerator", inContext: .loop) { b in
assert(b.context.contains(.loop))
b.continueNested(b.loadInt(Int64.random(in: 0...100)))
},

RecursiveCodeGenerator("TryCatchGenerator") { b in
// Build either try-catch-finally, try-catch, or try-finally
withEqualProbability({
Expand Down
8 changes: 8 additions & 0 deletions Sources/Fuzzilli/FuzzIL/Instruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,10 @@ extension Instruction: ProtobufConvertible {
$0.loopBreak = Fuzzilli_Protobuf_LoopBreak()
case .loopContinue:
$0.loopContinue = Fuzzilli_Protobuf_LoopContinue()
case .breakNested:
$0.breakNested = Fuzzilli_Protobuf_BreakNested()
case .continueNested:
$0.continueNested = Fuzzilli_Protobuf_ContinueNested()
case .beginTry:
$0.beginTry = Fuzzilli_Protobuf_BeginTry()
case .beginCatch:
Expand Down Expand Up @@ -1234,6 +1238,10 @@ extension Instruction: ProtobufConvertible {
op = LoopBreak()
case .loopContinue:
op = LoopContinue()
case .breakNested:
op = BreakNested()
case .continueNested:
op = ContinueNested()
case .beginTry:
op = BeginTry()
case .beginCatch:
Expand Down
16 changes: 16 additions & 0 deletions Sources/Fuzzilli/FuzzIL/JsOperations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2064,6 +2064,22 @@ final class LoopContinue: JsOperation {
}
}

final class BreakNested: JsOperation {
override var opcode: Opcode { .breakNested(self) }

init() {
super.init(numInputs: 1, attributes: [.isJump], requiredContext: [.javascript, .loop])
}
}

final class ContinueNested: JsOperation {
override var opcode: Opcode { .continueNested(self) }

init() {
super.init(numInputs: 1, attributes: [.isJump], requiredContext: [.javascript, .loop])
}
}

final class BeginTry: JsOperation {
override var opcode: Opcode { .beginTry(self) }

Expand Down
2 changes: 2 additions & 0 deletions Sources/Fuzzilli/FuzzIL/Opcodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ enum Opcode {
case endRepeatLoop(EndRepeatLoop)
case loopBreak(LoopBreak)
case loopContinue(LoopContinue)
case breakNested(BreakNested)
case continueNested(ContinueNested)
case beginTry(BeginTry)
case beginCatch(BeginCatch)
case beginFinally(BeginFinally)
Expand Down
6 changes: 6 additions & 0 deletions Sources/Fuzzilli/Lifting/FuzzILLifter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,12 @@ public class FuzzILLifter: Lifter {
case .loopContinue:
w.emit("Continue")

case .breakNested:
w.emit("BreakNested")

case .continueNested:
w.emit("ContinueNested")

case .beginTry:
w.emit("BeginTry")
w.increaseIndentionLevel()
Expand Down
101 changes: 100 additions & 1 deletion Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public class JavaScriptLifter: Lifter {
}
private var forLoopHeaderStack = Stack<ForLoopHeader>()

private var actualLoopDepth = 0

public init(prefix: String = "",
suffix: String = "",
ecmaVersion: ECMAScriptVersion) {
Expand Down Expand Up @@ -1059,14 +1061,20 @@ public class JavaScriptLifter: Lifter {

case .beginWhileLoopBody:
let COND = handleEndSingleExpressionContext(result: input(0), with: &w)
actualLoopDepth += 1
w.recordLoopPos()
w.emitBlock("while (\(COND)) {")
w.enterNewBlock()

case .endWhileLoop:
w.leaveCurrentBlock()
w.emit("}")
actualLoopDepth -= 1
w.popLoopPos()

case .beginDoWhileLoopBody:
actualLoopDepth += 1
w.recordLoopPos()
w.emit("do {")
w.enterNewBlock()

Expand All @@ -1077,6 +1085,8 @@ public class JavaScriptLifter: Lifter {
case .endDoWhileLoop:
let COND = handleEndSingleExpressionContext(result: input(0), with: &w)
w.emitBlock("} while (\(COND))")
actualLoopDepth -= 1
w.popLoopPos()

case .beginForLoopInitializer:
// While we could inline into the loop header, we probably don't want to do that as it will often lead
Expand Down Expand Up @@ -1152,7 +1162,8 @@ public class JavaScriptLifter: Lifter {
let INITIALIZER = header.initializer
var CONDITION = header.condition
var AFTERTHOUGHT = handleEndSingleExpressionContext(with: &w)

actualLoopDepth += 1
w.recordLoopPos()
if !INITIALIZER.contains("\n") && !CONDITION.contains("\n") && !AFTERTHOUGHT.contains("\n") {
if !CONDITION.isEmpty { CONDITION = " " + CONDITION }
if !AFTERTHOUGHT.isEmpty { AFTERTHOUGHT = " " + AFTERTHOUGHT }
Expand All @@ -1171,22 +1182,30 @@ public class JavaScriptLifter: Lifter {
case .endForLoop:
w.leaveCurrentBlock()
w.emit("}")
actualLoopDepth -= 1
w.popLoopPos()

case .beginForInLoop:
let LET = w.declarationKeyword(for: instr.innerOutput)
let V = w.declare(instr.innerOutput)
let OBJ = input(0)
actualLoopDepth += 1
w.recordLoopPos()
w.emit("for (\(LET) \(V) in \(OBJ)) {")
w.enterNewBlock()

case .endForInLoop:
w.leaveCurrentBlock()
w.emit("}")
actualLoopDepth -= 1
w.popLoopPos()

case .beginForOfLoop:
let V = w.declare(instr.innerOutput)
let LET = w.declarationKeyword(for: instr.innerOutput)
let OBJ = input(0)
actualLoopDepth += 1
w.recordLoopPos()
w.emit("for (\(LET) \(V) of \(OBJ)) {")
w.enterNewBlock()

Expand All @@ -1195,12 +1214,16 @@ public class JavaScriptLifter: Lifter {
let PATTERN = liftArrayDestructPattern(indices: op.indices, outputs: outputs, hasRestElement: op.hasRestElement)
let LET = w.varKeyword
let OBJ = input(0)
actualLoopDepth += 1
w.recordLoopPos()
w.emit("for (\(LET) [\(PATTERN)] of \(OBJ)) {")
w.enterNewBlock()

case .endForOfLoop:
w.leaveCurrentBlock()
w.emit("}")
actualLoopDepth -= 1
w.popLoopPos()

case .beginRepeatLoop(let op):
let LET = w.varKeyword
Expand All @@ -1211,12 +1234,16 @@ public class JavaScriptLifter: Lifter {
I = "i"
}
let ITERATIONS = op.iterations
actualLoopDepth += 1
w.recordLoopPos()
w.emit("for (\(LET) \(I) = 0; \(I) < \(ITERATIONS); \(I)++) {")
w.enterNewBlock()

case .endRepeatLoop:
w.leaveCurrentBlock()
w.emit("}")
actualLoopDepth -= 1
w.popLoopPos()

case .loopBreak(_),
.switchBreak:
Expand All @@ -1225,6 +1252,34 @@ public class JavaScriptLifter: Lifter {
case .loopContinue:
w.emit("continue;")

case .breakNested(_):
let input = input(0)
let expectedDepth = Int(input.text) ?? 0
let d = expectedDepth % actualLoopDepth
let pos = w.getLoopPos(d)
let pre = String(repeating: " ", count: 4 * d)
let s = pre + "label" + String(d) + ":\n"
if(!w.getLabelExist(d)){
w.insertLabel(pos, s)
w.setLabelExist(d)
w.updateLoopPos(d + 1, s.length)
}
w.emit("break " + "label" + String(d) + ";")

case .continueNested:
let input = input(0)
let expectedDepth = Int(input.text) ?? 0
let d = expectedDepth % actualLoopDepth
let pos = w.getLoopPos(d)
let pre = String(repeating: " ", count: 4 * d)
let s = pre + "label" + String(d) + ":\n"
if(!w.getLabelExist(d)){
w.insertLabel(pos, s)
w.setLabelExist(d)
w.updateLoopPos(d + 1, s.length)
}
w.emit("continue " + "label" + String(d) + ";")

case .beginTry:
w.emit("try {")
w.enterNewBlock()
Expand Down Expand Up @@ -1514,6 +1569,12 @@ public class JavaScriptLifter: Lifter {
return writer.code
}

struct LoopPosInfo {
var loopBeginPos: Int
var exist: Bool
}
private var loopPos: [LoopPosInfo]

// Maps each FuzzIL variable to its JavaScript expression.
// The expression for a FuzzIL variable can generally either be
// * an identifier like "v42" if the FuzzIL variable is mapped to a JavaScript variable OR
Expand All @@ -1534,6 +1595,7 @@ public class JavaScriptLifter: Lifter {
self.analyzer = analyzer
self.varKeyword = version == .es6 ? "let" : "var"
self.constKeyword = version == .es6 ? "const" : "var"
self.loopPos = []
}

/// Assign a JavaScript expression to a FuzzIL variable.
Expand Down Expand Up @@ -1771,6 +1833,43 @@ public class JavaScriptLifter: Lifter {
writer.emit(line)
}

mutating func recordLoopPos(){
loopPos.append(LoopPosInfo(loopBeginPos: code.count, exist: false ))
}

mutating func popLoopPos(){
loopPos.popLast()
}

mutating func getLoopPos(_ idx: Int) -> Int{
return loopPos[idx].loopBeginPos
}

// if we insert one label into code, then the after record index move the same length
mutating func updateLoopPos(_ startIndex: Int, _ len: Int){
if(startIndex <= loopPos.count - 1){
for i in startIndex...loopPos.count - 1 {
loopPos[i].loopBeginPos += len
}
}
}

mutating func getLabelExist(_ idx: Int) -> Bool{
return loopPos[idx].exist
}

mutating func setLabelExist(_ idx: Int){
loopPos[idx].exist = true
}

mutating func clearLoopPos(){
loopPos = []
}

mutating func insertLabel(_ pos: Int, _ content: String){
writer.insert(pos, content)
}

/// Emit a (potentially multi-line) comment.
mutating func emitComment(_ comment: String) {
writer.emitComment(comment)
Expand Down
8 changes: 8 additions & 0 deletions Sources/Fuzzilli/Lifting/ScriptWriter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,12 @@ struct ScriptWriter {
assert(currentIndention.count >= indent.count)
currentIndention.removeLast(indent.count)
}

mutating func insert(_ pos: Int, _ content: String){
if code.index(code.startIndex, offsetBy: pos, limitedBy: code.endIndex) != nil {
let index = code.index(code.startIndex, offsetBy: pos)
code.insert(contentsOf: content, at: index)
currentLineNumber += 1
}
}
}
Loading

0 comments on commit facb0f9

Please sign in to comment.