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

feature/labelInLoop #479

Open
wants to merge 1 commit into
base: main
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
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 loopBreakNested(_ depth: Int){
emit(LoopBreakNested(depth), withInputs: [])
}

public func loopContinueNested(_ depth: Int){
emit(LoopContinueNested(depth), withInputs: [])
}

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 @@ -164,6 +164,8 @@ public let codeGeneratorWeights = [
"RepeatLoopGenerator": 10,
"SwitchCaseBreakGenerator": 5,
"LoopBreakGenerator": 5,
"LoopLabelBreakGenerator": 3,
"LoopLabelContinueGenerator": 3,
"ContinueGenerator": 5,
"TryCatchGenerator": 5,
"ThrowGenerator": 1,
Expand Down
8 changes: 8 additions & 0 deletions Sources/Fuzzilli/CodeGen/CodeGenerators.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,14 @@ public let CodeGenerators: [CodeGenerator] = [
b.loopContinue()
},

CodeGenerator("LoopLabelBreakGenerator", inContext: .loop) { b in
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: maybe these should also be called LoopNestedBreakGenerator and LoopNestedContinueGenerator?

b.loopBreakNested(Int.random(in: 0...10))
},

CodeGenerator("LoopLabelContinueGenerator", inContext: .loop) { b in
b.loopContinueNested(Int.random(in: 0...10))
},

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 .loopBreakNested:
$0.loopBreakNested = Fuzzilli_Protobuf_LoopBreakNested()
case .loopContinueNested:
$0.loopContinueNested = Fuzzilli_Protobuf_LoopContinueNested()
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 .loopBreakNested(let d):
op = LoopBreakNested(d.depth)
case .loopContinueNested(let d):
op = LoopContinueNested(d.depth)
case .beginTry:
op = BeginTry()
case .beginCatch:
Expand Down
20 changes: 20 additions & 0 deletions Sources/Fuzzilli/FuzzIL/JsOperations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2064,6 +2064,26 @@ final class LoopContinue: JsOperation {
}
}

final class LoopBreakNested: JsOperation {
override var opcode: Opcode { .loopBreakNested(self) }

let depth: Int
init(_ depth: Int) {
self.depth = depth
super.init(attributes: [.isJump], requiredContext: [.javascript, .loop])
}
}

final class LoopContinueNested: JsOperation {
override var opcode: Opcode { .loopContinueNested(self) }

let depth: Int
init(_ depth: Int) {
self.depth = depth
super.init(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 loopBreakNested(LoopBreakNested)
case loopContinueNested(LoopContinueNested)
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 .loopBreakNested:
w.emit("LoopBreakNested")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: maybe add the depth here and below:

        case .loopBreakNested(let op):
            w.emit("LoopBreakNested \(op.depth)")


case .loopContinueNested:
w.emit("LoopContinueNested")

case .beginTry:
w.emit("BeginTry")
w.increaseIndentionLevel()
Expand Down
99 changes: 98 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,32 @@ public class JavaScriptLifter: Lifter {
case .loopContinue:
w.emit("continue;")

case .loopBreakNested(let op):
let expectedDepth = op.depth
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 .loopContinueNested(let op):
let expectedDepth = op.depth
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 +1567,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 +1593,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 +1831,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
}
}
}
4 changes: 4 additions & 0 deletions Sources/Fuzzilli/Mutators/OperationMutator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ public class OperationMutator: BaseInstructionMutator {
newOp = UpdateSuperProperty(propertyName: b.randomPropertyName(), operator: chooseUniform(from: BinaryOperator.allCases))
case .beginIf(let op):
newOp = BeginIf(inverted: !op.inverted)
case .loopBreakNested(let op):
newOp = LoopBreakNested(Int.random(in: 0...10))
case .loopContinueNested(let op):
newOp = LoopContinueNested(Int.random(in: 0...10))
default:
fatalError("Unhandled Operation: \(type(of: instr.op))")
}
Expand Down
Loading
Loading