Skip to content

Commit 0f003d9

Browse files
committed
feature/labelInLoop
1 parent d4796ed commit 0f003d9

15 files changed

+571
-1
lines changed

Sources/Fuzzilli/Base/ProgramBuilder.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2531,6 +2531,14 @@ public class ProgramBuilder {
25312531
emit(LoopContinue(), withInputs: [])
25322532
}
25332533

2534+
public func loopBreakNested(_ depth: Int){
2535+
emit(LoopBreakNested(depth), withInputs: [])
2536+
}
2537+
2538+
public func loopContinueNested(_ depth: Int){
2539+
emit(LoopContinueNested(depth), withInputs: [])
2540+
}
2541+
25342542
public func buildTryCatchFinally(tryBody: () -> (), catchBody: ((Variable) -> ())? = nil, finallyBody: (() -> ())? = nil) {
25352543
assert(catchBody != nil || finallyBody != nil, "Must have either a Catch or a Finally block (or both)")
25362544
emit(BeginTry())

Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ public let codeGeneratorWeights = [
164164
"RepeatLoopGenerator": 10,
165165
"SwitchCaseBreakGenerator": 5,
166166
"LoopBreakGenerator": 5,
167+
"LoopLabelBreakGenerator": 3,
168+
"LoopLabelContinueGenerator": 3,
167169
"ContinueGenerator": 5,
168170
"TryCatchGenerator": 5,
169171
"ThrowGenerator": 1,

Sources/Fuzzilli/CodeGen/CodeGenerators.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,14 @@ public let CodeGenerators: [CodeGenerator] = [
14151415
b.loopContinue()
14161416
},
14171417

1418+
CodeGenerator("LoopLabelBreakGenerator", inContext: .loop) { b in
1419+
b.loopBreakNested(Int.random(in: 0...10))
1420+
},
1421+
1422+
CodeGenerator("LoopLabelContinueGenerator", inContext: .loop) { b in
1423+
b.loopContinueNested(Int.random(in: 0...10))
1424+
},
1425+
14181426
RecursiveCodeGenerator("TryCatchGenerator") { b in
14191427
// Build either try-catch-finally, try-catch, or try-finally
14201428
withEqualProbability({

Sources/Fuzzilli/FuzzIL/Instruction.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,10 @@ extension Instruction: ProtobufConvertible {
816816
$0.loopBreak = Fuzzilli_Protobuf_LoopBreak()
817817
case .loopContinue:
818818
$0.loopContinue = Fuzzilli_Protobuf_LoopContinue()
819+
case .loopBreakNested:
820+
$0.loopBreakNested = Fuzzilli_Protobuf_LoopBreakNested()
821+
case .loopContinueNested:
822+
$0.loopContinueNested = Fuzzilli_Protobuf_LoopContinueNested()
819823
case .beginTry:
820824
$0.beginTry = Fuzzilli_Protobuf_BeginTry()
821825
case .beginCatch:
@@ -1234,6 +1238,10 @@ extension Instruction: ProtobufConvertible {
12341238
op = LoopBreak()
12351239
case .loopContinue:
12361240
op = LoopContinue()
1241+
case .loopBreakNested(let d):
1242+
op = LoopBreakNested(d.depth)
1243+
case .loopContinueNested(let d):
1244+
op = LoopContinueNested(d.depth)
12371245
case .beginTry:
12381246
op = BeginTry()
12391247
case .beginCatch:

Sources/Fuzzilli/FuzzIL/JsOperations.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,6 +2064,26 @@ final class LoopContinue: JsOperation {
20642064
}
20652065
}
20662066

2067+
final class LoopBreakNested: JsOperation {
2068+
override var opcode: Opcode { .loopBreakNested(self) }
2069+
2070+
let depth: Int
2071+
init(_ depth: Int) {
2072+
self.depth = depth
2073+
super.init(attributes: [.isJump], requiredContext: [.javascript, .loop])
2074+
}
2075+
}
2076+
2077+
final class LoopContinueNested: JsOperation {
2078+
override var opcode: Opcode { .loopContinueNested(self) }
2079+
2080+
let depth: Int
2081+
init(_ depth: Int) {
2082+
self.depth = depth
2083+
super.init(attributes: [.isJump], requiredContext: [.javascript, .loop])
2084+
}
2085+
}
2086+
20672087
final class BeginTry: JsOperation {
20682088
override var opcode: Opcode { .beginTry(self) }
20692089

Sources/Fuzzilli/FuzzIL/Opcodes.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ enum Opcode {
186186
case endRepeatLoop(EndRepeatLoop)
187187
case loopBreak(LoopBreak)
188188
case loopContinue(LoopContinue)
189+
case loopBreakNested(LoopBreakNested)
190+
case loopContinueNested(LoopContinueNested)
189191
case beginTry(BeginTry)
190192
case beginCatch(BeginCatch)
191193
case beginFinally(BeginFinally)

Sources/Fuzzilli/Lifting/FuzzILLifter.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,12 @@ public class FuzzILLifter: Lifter {
709709
case .loopContinue:
710710
w.emit("Continue")
711711

712+
case .loopBreakNested:
713+
w.emit("LoopBreakNested")
714+
715+
case .loopContinueNested:
716+
w.emit("LoopContinueNested")
717+
712718
case .beginTry:
713719
w.emit("BeginTry")
714720
w.increaseIndentionLevel()

Sources/Fuzzilli/Lifting/JavaScriptLifter.swift

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public class JavaScriptLifter: Lifter {
4343
}
4444
private var forLoopHeaderStack = Stack<ForLoopHeader>()
4545

46+
private var actualLoopDepth = 0
47+
4648
public init(prefix: String = "",
4749
suffix: String = "",
4850
ecmaVersion: ECMAScriptVersion) {
@@ -1059,14 +1061,20 @@ public class JavaScriptLifter: Lifter {
10591061

10601062
case .beginWhileLoopBody:
10611063
let COND = handleEndSingleExpressionContext(result: input(0), with: &w)
1064+
actualLoopDepth += 1
1065+
w.recordLoopPos()
10621066
w.emitBlock("while (\(COND)) {")
10631067
w.enterNewBlock()
10641068

10651069
case .endWhileLoop:
10661070
w.leaveCurrentBlock()
10671071
w.emit("}")
1072+
actualLoopDepth -= 1
1073+
w.popLoopPos()
10681074

10691075
case .beginDoWhileLoopBody:
1076+
actualLoopDepth += 1
1077+
w.recordLoopPos()
10701078
w.emit("do {")
10711079
w.enterNewBlock()
10721080

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

10811091
case .beginForLoopInitializer:
10821092
// While we could inline into the loop header, we probably don't want to do that as it will often lead
@@ -1152,7 +1162,8 @@ public class JavaScriptLifter: Lifter {
11521162
let INITIALIZER = header.initializer
11531163
var CONDITION = header.condition
11541164
var AFTERTHOUGHT = handleEndSingleExpressionContext(with: &w)
1155-
1165+
actualLoopDepth += 1
1166+
w.recordLoopPos()
11561167
if !INITIALIZER.contains("\n") && !CONDITION.contains("\n") && !AFTERTHOUGHT.contains("\n") {
11571168
if !CONDITION.isEmpty { CONDITION = " " + CONDITION }
11581169
if !AFTERTHOUGHT.isEmpty { AFTERTHOUGHT = " " + AFTERTHOUGHT }
@@ -1171,22 +1182,30 @@ public class JavaScriptLifter: Lifter {
11711182
case .endForLoop:
11721183
w.leaveCurrentBlock()
11731184
w.emit("}")
1185+
actualLoopDepth -= 1
1186+
w.popLoopPos()
11741187

11751188
case .beginForInLoop:
11761189
let LET = w.declarationKeyword(for: instr.innerOutput)
11771190
let V = w.declare(instr.innerOutput)
11781191
let OBJ = input(0)
1192+
actualLoopDepth += 1
1193+
w.recordLoopPos()
11791194
w.emit("for (\(LET) \(V) in \(OBJ)) {")
11801195
w.enterNewBlock()
11811196

11821197
case .endForInLoop:
11831198
w.leaveCurrentBlock()
11841199
w.emit("}")
1200+
actualLoopDepth -= 1
1201+
w.popLoopPos()
11851202

11861203
case .beginForOfLoop:
11871204
let V = w.declare(instr.innerOutput)
11881205
let LET = w.declarationKeyword(for: instr.innerOutput)
11891206
let OBJ = input(0)
1207+
actualLoopDepth += 1
1208+
w.recordLoopPos()
11901209
w.emit("for (\(LET) \(V) of \(OBJ)) {")
11911210
w.enterNewBlock()
11921211

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

12011222
case .endForOfLoop:
12021223
w.leaveCurrentBlock()
12031224
w.emit("}")
1225+
actualLoopDepth -= 1
1226+
w.popLoopPos()
12041227

12051228
case .beginRepeatLoop(let op):
12061229
let LET = w.varKeyword
@@ -1211,12 +1234,16 @@ public class JavaScriptLifter: Lifter {
12111234
I = "i"
12121235
}
12131236
let ITERATIONS = op.iterations
1237+
actualLoopDepth += 1
1238+
w.recordLoopPos()
12141239
w.emit("for (\(LET) \(I) = 0; \(I) < \(ITERATIONS); \(I)++) {")
12151240
w.enterNewBlock()
12161241

12171242
case .endRepeatLoop:
12181243
w.leaveCurrentBlock()
12191244
w.emit("}")
1245+
actualLoopDepth -= 1
1246+
w.popLoopPos()
12201247

12211248
case .loopBreak(_),
12221249
.switchBreak:
@@ -1225,6 +1252,32 @@ public class JavaScriptLifter: Lifter {
12251252
case .loopContinue:
12261253
w.emit("continue;")
12271254

1255+
case .loopBreakNested(let op):
1256+
let expectedDepth = op.depth
1257+
let d = expectedDepth % actualLoopDepth
1258+
let pos = w.getLoopPos(d)
1259+
let pre = String(repeating: " ", count: 4 * d)
1260+
let s = pre + "label" + String(d) + ":\n"
1261+
if(!w.getLabelExist(d)){
1262+
w.insertLabel(pos, s)
1263+
w.setLabelExist(d)
1264+
w.updateLoopPos(d + 1, s.length)
1265+
}
1266+
w.emit("break " + "label" + String(d) + ";")
1267+
1268+
case .loopContinueNested(let op):
1269+
let expectedDepth = op.depth
1270+
let d = expectedDepth % actualLoopDepth
1271+
let pos = w.getLoopPos(d)
1272+
let pre = String(repeating: " ", count: 4 * d)
1273+
let s = pre + "label" + String(d) + ":\n"
1274+
if(!w.getLabelExist(d)){
1275+
w.insertLabel(pos, s)
1276+
w.setLabelExist(d)
1277+
w.updateLoopPos(d + 1, s.length)
1278+
}
1279+
w.emit("continue " + "label" + String(d) + ";")
1280+
12281281
case .beginTry:
12291282
w.emit("try {")
12301283
w.enterNewBlock()
@@ -1514,6 +1567,12 @@ public class JavaScriptLifter: Lifter {
15141567
return writer.code
15151568
}
15161569

1570+
struct LoopPosInfo {
1571+
var loopBeginPos: Int
1572+
var exist: Bool
1573+
}
1574+
private var loopPos: [LoopPosInfo]
1575+
15171576
// Maps each FuzzIL variable to its JavaScript expression.
15181577
// The expression for a FuzzIL variable can generally either be
15191578
// * an identifier like "v42" if the FuzzIL variable is mapped to a JavaScript variable OR
@@ -1534,6 +1593,7 @@ public class JavaScriptLifter: Lifter {
15341593
self.analyzer = analyzer
15351594
self.varKeyword = version == .es6 ? "let" : "var"
15361595
self.constKeyword = version == .es6 ? "const" : "var"
1596+
self.loopPos = []
15371597
}
15381598

15391599
/// Assign a JavaScript expression to a FuzzIL variable.
@@ -1771,6 +1831,43 @@ public class JavaScriptLifter: Lifter {
17711831
writer.emit(line)
17721832
}
17731833

1834+
mutating func recordLoopPos(){
1835+
loopPos.append(LoopPosInfo(loopBeginPos: code.count, exist: false ))
1836+
}
1837+
1838+
mutating func popLoopPos(){
1839+
loopPos.popLast()
1840+
}
1841+
1842+
mutating func getLoopPos(_ idx: Int) -> Int{
1843+
return loopPos[idx].loopBeginPos
1844+
}
1845+
1846+
// if we insert one label into code, then the after record index move the same length
1847+
mutating func updateLoopPos(_ startIndex: Int, _ len: Int){
1848+
if(startIndex <= loopPos.count - 1){
1849+
for i in startIndex...loopPos.count - 1 {
1850+
loopPos[i].loopBeginPos += len
1851+
}
1852+
}
1853+
}
1854+
1855+
mutating func getLabelExist(_ idx: Int) -> Bool{
1856+
return loopPos[idx].exist
1857+
}
1858+
1859+
mutating func setLabelExist(_ idx: Int){
1860+
loopPos[idx].exist = true
1861+
}
1862+
1863+
mutating func clearLoopPos(){
1864+
loopPos = []
1865+
}
1866+
1867+
mutating func insertLabel(_ pos: Int, _ content: String){
1868+
writer.insert(pos, content)
1869+
}
1870+
17741871
/// Emit a (potentially multi-line) comment.
17751872
mutating func emitComment(_ comment: String) {
17761873
writer.emitComment(comment)

Sources/Fuzzilli/Lifting/ScriptWriter.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,12 @@ struct ScriptWriter {
8484
assert(currentIndention.count >= indent.count)
8585
currentIndention.removeLast(indent.count)
8686
}
87+
88+
mutating func insert(_ pos: Int, _ content: String){
89+
if code.index(code.startIndex, offsetBy: pos, limitedBy: code.endIndex) != nil {
90+
let index = code.index(code.startIndex, offsetBy: pos)
91+
code.insert(contentsOf: content, at: index)
92+
currentLineNumber += 1
93+
}
94+
}
8795
}

Sources/Fuzzilli/Mutators/OperationMutator.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ public class OperationMutator: BaseInstructionMutator {
227227
newOp = UpdateSuperProperty(propertyName: b.randomPropertyName(), operator: chooseUniform(from: BinaryOperator.allCases))
228228
case .beginIf(let op):
229229
newOp = BeginIf(inverted: !op.inverted)
230+
case .loopBreakNested(let op):
231+
newOp = LoopBreakNested(Int.random(in: 0...10))
232+
case .loopContinueNested(let op):
233+
newOp = LoopContinueNested(Int.random(in: 0...10))
230234
default:
231235
fatalError("Unhandled Operation: \(type(of: instr.op))")
232236
}

0 commit comments

Comments
 (0)