Skip to content

Commit e0e81c9

Browse files
committed
feature/loopNestedContinueFixed
1 parent f31876f commit e0e81c9

15 files changed

+238
-3
lines changed

Sources/Fuzzilli/Base/ProgramBuilder.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2822,6 +2822,9 @@ public class ProgramBuilder {
28222822
emit(Print(), withInputs: [value])
28232823
}
28242824

2825+
public func loopNestedContinue(_ depth: Int){
2826+
emit(LoopNestedContinue(depth), withInputs: [])
2827+
}
28252828

28262829
@discardableResult
28272830
public func createWasmGlobal(value: WasmGlobal, isMutable: Bool) -> Variable {

Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ public let codeGeneratorWeights = [
169169
"TryCatchGenerator": 5,
170170
"ThrowGenerator": 1,
171171
"BlockStatementGenerator": 1,
172+
"LoopNestedContinueGenerator": 1,
172173

173174
// Special generators
174175
"WellKnownPropertyLoadGenerator": 5,

Sources/Fuzzilli/CodeGen/CodeGenerators.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1866,6 +1866,10 @@ public let CodeGenerators: [CodeGenerator] = [
18661866
b.callFunction(f, withArgs: args)
18671867
}, catchBody: { _ in })
18681868
},
1869+
1870+
CodeGenerator("LoopNestedContinueGenerator", inContext: .loop) { b in
1871+
b.loopNestedContinue(Int.random(in: 0...10))
1872+
}
18691873
]
18701874

18711875
extension Array where Element == CodeGenerator {

Sources/Fuzzilli/FuzzIL/Instruction.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,8 @@ extension Instruction: ProtobufConvertible {
10301030
$0.wrapSuspending = Fuzzilli_Protobuf_WrapSuspending()
10311031
case .bindMethod(let op):
10321032
$0.bindMethod = Fuzzilli_Protobuf_BindMethod.with { $0.methodName = op.methodName }
1033+
case .loopNestedContinue:
1034+
$0.loopNestedContinue = Fuzzilli_Protobuf_LoopNestedContinue()
10331035
case .print(_):
10341036
fatalError("Print operations should not be serialized")
10351037
// Wasm Operations
@@ -1892,6 +1894,8 @@ extension Instruction: ProtobufConvertible {
18921894
op = LoadNewTarget()
18931895
case .nop:
18941896
op = Nop()
1897+
case .loopNestedContinue(let d):
1898+
op = LoopNestedContinue(d.depth)
18951899
case .createWasmGlobal(let p):
18961900
op = CreateWasmGlobal(value: convertWasmGlobal(p.wasmGlobal), isMutable: p.wasmGlobal.isMutable)
18971901
case .createWasmMemory(let p):

Sources/Fuzzilli/FuzzIL/JsOperations.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2463,6 +2463,17 @@ class BindMethod: JsOperation {
24632463
super.init(numInputs: 1, numOutputs: 1, requiredContext: .javascript)
24642464
}
24652465
}
2466+
// Assuming the current nested depth is D, then depth % D is the actual level that can be continued, and a label will be generated at that level for continue
2467+
final class LoopNestedContinue: JsOperation {
2468+
override var opcode: Opcode { .loopNestedContinue(self) }
2469+
2470+
let depth: Int
2471+
2472+
init(_ depth: Int) {
2473+
self.depth = depth
2474+
super.init(attributes: [.isJump], requiredContext: [.javascript, .loop])
2475+
}
2476+
}
24662477

24672478

24682479
// This instruction is used to create strongly typed WasmGlobals in the JS world that can be imported by a WasmModule.

Sources/Fuzzilli/FuzzIL/Opcodes.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ enum Opcode {
202202
case endSwitch(EndSwitch)
203203
case switchBreak(SwitchBreak)
204204
case loadNewTarget(LoadNewTarget)
205+
case loopNestedContinue(LoopNestedContinue)
205206
case print(Print)
206207
case explore(Explore)
207208
case probe(Probe)

Sources/Fuzzilli/Lifting/FuzzILLifter.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,9 @@ public class FuzzILLifter: Lifter {
753753
case .loadNewTarget:
754754
w.emit("\(output()) <- LoadNewTarget")
755755

756+
case .loopNestedContinue(let op):
757+
w.emit("LoopNestedContinue \(op.depth)")
758+
756759
case .beginWasmModule:
757760
w.emit("BeginWasmModule")
758761
w.increaseIndentionLevel()

Sources/Fuzzilli/Lifting/JavaScriptLifter.swift

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,14 +1150,17 @@ public class JavaScriptLifter: Lifter {
11501150

11511151
case .beginWhileLoopBody:
11521152
let COND = handleEndSingleExpressionContext(result: input(0), with: &w)
1153+
w.pushLabelStack(LabelType.loopblock)
11531154
w.emitBlock("while (\(COND)) {")
11541155
w.enterNewBlock()
11551156

11561157
case .endWhileLoop:
11571158
w.leaveCurrentBlock()
11581159
w.emit("}")
1160+
w.popLabelStack(LabelType.loopblock)
11591161

11601162
case .beginDoWhileLoopBody:
1163+
w.pushLabelStack(LabelType.loopblock)
11611164
w.emit("do {")
11621165
w.enterNewBlock()
11631166

@@ -1168,6 +1171,7 @@ public class JavaScriptLifter: Lifter {
11681171
case .endDoWhileLoop:
11691172
let COND = handleEndSingleExpressionContext(result: input(0), with: &w)
11701173
w.emitBlock("} while (\(COND))")
1174+
w.popLabelStack(LabelType.loopblock)
11711175

11721176
case .beginForLoopInitializer:
11731177
// While we could inline into the loop header, we probably don't want to do that as it will often lead
@@ -1243,6 +1247,7 @@ public class JavaScriptLifter: Lifter {
12431247
let INITIALIZER = header.initializer
12441248
var CONDITION = header.condition
12451249
var AFTERTHOUGHT = handleEndSingleExpressionContext(with: &w)
1250+
w.pushLabelStack(LabelType.loopblock)
12461251

12471252
if !INITIALIZER.contains("\n") && !CONDITION.contains("\n") && !AFTERTHOUGHT.contains("\n") {
12481253
if !CONDITION.isEmpty { CONDITION = " " + CONDITION }
@@ -1262,22 +1267,26 @@ public class JavaScriptLifter: Lifter {
12621267
case .endForLoop:
12631268
w.leaveCurrentBlock()
12641269
w.emit("}")
1270+
w.popLabelStack(LabelType.loopblock)
12651271

12661272
case .beginForInLoop:
12671273
let LET = w.declarationKeyword(for: instr.innerOutput)
12681274
let V = w.declare(instr.innerOutput)
12691275
let OBJ = input(0)
1276+
w.pushLabelStack(LabelType.loopblock)
12701277
w.emit("for (\(LET) \(V) in \(OBJ)) {")
12711278
w.enterNewBlock()
12721279

12731280
case .endForInLoop:
12741281
w.leaveCurrentBlock()
12751282
w.emit("}")
1283+
w.popLabelStack(LabelType.loopblock)
12761284

12771285
case .beginForOfLoop:
12781286
let V = w.declare(instr.innerOutput)
12791287
let LET = w.declarationKeyword(for: instr.innerOutput)
12801288
let OBJ = input(0)
1289+
w.pushLabelStack(LabelType.loopblock)
12811290
w.emit("for (\(LET) \(V) of \(OBJ)) {")
12821291
w.enterNewBlock()
12831292

@@ -1286,12 +1295,14 @@ public class JavaScriptLifter: Lifter {
12861295
let PATTERN = liftArrayDestructPattern(indices: op.indices, outputs: outputs, hasRestElement: op.hasRestElement)
12871296
let LET = w.varKeyword
12881297
let OBJ = input(0)
1298+
w.pushLabelStack(LabelType.loopblock)
12891299
w.emit("for (\(LET) [\(PATTERN)] of \(OBJ)) {")
12901300
w.enterNewBlock()
12911301

12921302
case .endForOfLoop:
12931303
w.leaveCurrentBlock()
12941304
w.emit("}")
1305+
w.popLabelStack(LabelType.loopblock)
12951306

12961307
case .beginRepeatLoop(let op):
12971308
let LET = w.varKeyword
@@ -1302,12 +1313,14 @@ public class JavaScriptLifter: Lifter {
13021313
I = "i"
13031314
}
13041315
let ITERATIONS = op.iterations
1316+
w.pushLabelStack(LabelType.loopblock)
13051317
w.emit("for (\(LET) \(I) = 0; \(I) < \(ITERATIONS); \(I)++) {")
13061318
w.enterNewBlock()
13071319

13081320
case .endRepeatLoop:
13091321
w.leaveCurrentBlock()
13101322
w.emit("}")
1323+
w.popLabelStack(LabelType.loopblock)
13111324

13121325
case .loopBreak(_),
13131326
.switchBreak:
@@ -1372,6 +1385,9 @@ public class JavaScriptLifter: Lifter {
13721385
let VALUE = input(0)
13731386
w.emit("fuzzilli('FUZZILLI_PRINT', \(VALUE));")
13741387

1388+
case .loopNestedContinue(let op):
1389+
w.liftContinueLabel(LabelType.loopblock, expDepth: op.depth)
1390+
13751391
case .createWasmGlobal(let op):
13761392
let V = w.declare(instr.output)
13771393
let LET = w.varKeyword
@@ -1640,6 +1656,7 @@ public class JavaScriptLifter: Lifter {
16401656
w.emitComment(footer)
16411657
}
16421658

1659+
assert(w.labelStackDepth(LabelType.loopblock) == 0)
16431660
return w.code
16441661
}
16451662

@@ -1846,6 +1863,11 @@ public class JavaScriptLifter: Lifter {
18461863
return writer.code
18471864
}
18481865

1866+
// Used for nestBreak series operations to record location information during append code, where temporary data structures such as temporaryOutputBufferStack may not necessarily be empty
1867+
var codeLength: Int {
1868+
return writer.code.count
1869+
}
1870+
18491871
// Maps each FuzzIL variable to its JavaScript expression.
18501872
// The expression for a FuzzIL variable can generally either be
18511873
// * an identifier like "v42" if the FuzzIL variable is mapped to a JavaScript variable OR
@@ -1861,6 +1883,11 @@ public class JavaScriptLifter: Lifter {
18611883
// See `reassign()` for more details about reassignment inlining.
18621884
private var inlinedReassignments = VariableMap<Expression>()
18631885

1886+
// Trace nested code block to break/continue a label
1887+
private var labelStack: [LabelType: [LabelPin]] = [
1888+
LabelType.loopblock: []
1889+
]
1890+
18641891
init(analyzer: DefUseAnalyzer, version: ECMAScriptVersion, stripComments: Bool = false, includeLineNumbers: Bool = false, indent: Int = 4) {
18651892
self.writer = ScriptWriter(stripComments: stripComments, includeLineNumbers: includeLineNumbers, indent: indent)
18661893
self.analyzer = analyzer
@@ -2236,6 +2263,66 @@ public class JavaScriptLifter: Lifter {
22362263
return analyzer.numUses(of: v) <= 1
22372264
}
22382265
}
2266+
2267+
/// Records a new label pin at the current code position.
2268+
mutating func pushLabelStack(_ labelStackType: LabelType) {
2269+
labelStack[labelStackType, default: []].append(
2270+
LabelPin(
2271+
beginPos: codeLength,
2272+
hasLabel: false,
2273+
indention: writer.getCurrentIndention()
2274+
)
2275+
)
2276+
}
2277+
2278+
/// Removes the most recently recorded label pin.
2279+
mutating func popLabelStack(_ labelStackType: LabelType) {
2280+
_ = labelStack[labelStackType, default: []].popLast()
2281+
}
2282+
2283+
/// Checks whether a label has already been inserted at the specified index.
2284+
private mutating func labelExists(_ labelStack: inout [LabelPin], at index: Int) -> Bool {
2285+
return labelStack[index].hasLabel
2286+
}
2287+
2288+
/// Updates the label stack when a label is inserted into the code.
2289+
///
2290+
/// This method:
2291+
/// - Marks the label at the specified index as inserted.
2292+
/// - Inserts the label content at the given code position.
2293+
/// - Shifts the positions of subsequent labels accordingly.
2294+
private mutating func insertLabel(_ type: LabelType, _ index: Int, _ labelContent: String) {
2295+
var stack = labelStack[type]!
2296+
let insertPos = stack[index].beginPos
2297+
let indention = stack[index].indention
2298+
2299+
writer.insert(insertPos, labelContent, indention)
2300+
2301+
stack[index].hasLabel = true
2302+
let delta = labelContent.count
2303+
2304+
for i in index+1..<stack.count {
2305+
stack[i].beginPos += delta
2306+
}
2307+
2308+
labelStack[type] = stack
2309+
}
2310+
2311+
mutating func liftContinueLabel(_ type: LabelType, expDepth: Int) {
2312+
let stack = labelStack[type]!
2313+
let d = expDepth % stack.count
2314+
let labelName = "label\(d):"
2315+
2316+
if !stack[d].hasLabel {
2317+
insertLabel(type, d, labelName)
2318+
}
2319+
2320+
emit("continue label\(d);")
2321+
}
2322+
2323+
mutating func labelStackDepth(_ type: LabelType) -> Int {
2324+
return labelStack[type]!.count
2325+
}
22392326
}
22402327

22412328
// Helper class for formatting object literals.
@@ -2268,4 +2355,15 @@ public class JavaScriptLifter: Lifter {
22682355
fields[fields.count - 1] += body + "}"
22692356
}
22702357
}
2358+
2359+
// Every possible position of label
2360+
struct LabelPin {
2361+
var beginPos: Int
2362+
var hasLabel: Bool
2363+
var indention: String
2364+
}
2365+
2366+
enum LabelType {
2367+
case loopblock
2368+
}
22712369
}

Sources/Fuzzilli/Lifting/ScriptWriter.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,19 @@ struct ScriptWriter {
8484
assert(currentIndention.count >= indent.count)
8585
currentIndention.removeLast(indent.count)
8686
}
87-
}
87+
88+
/// Insert one or more lines of code
89+
mutating func insert(_ pos: Int, _ content: String, _ indention: String) {
90+
assert(!content.contains("\n"))
91+
assert(pos >= 0 && pos <= code.count)
92+
93+
var lineNumHeaderCnt = 0
94+
if includeLineNumbers { lineNumHeaderCnt = "\(String(format: "%3i", currentLineNumber)). ".count }
95+
let index = code.index(code.startIndex, offsetBy: pos + indention.count + lineNumHeaderCnt)
96+
code.insert(contentsOf: content, at: index)
97+
}
98+
99+
mutating func getCurrentIndention() -> String{
100+
return currentIndention
101+
}
102+
}

Sources/Fuzzilli/Mutators/OperationMutator.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ public class OperationMutator: BaseInstructionMutator {
221221
newOp = UpdateSuperProperty(propertyName: b.randomPropertyName(), operator: chooseUniform(from: BinaryOperator.allCases))
222222
case .beginIf(let op):
223223
newOp = BeginIf(inverted: !op.inverted)
224+
case .loopNestedContinue(let op):
225+
newOp = LoopNestedContinue(Int.random(in: 0...10))
224226
case .createWasmGlobal(let op):
225227
// The type has to match for wasm, we cannot just switch types here as the rest of the wasm code will become invalid.
226228
// TODO: add nullref and funcref as types here.

0 commit comments

Comments
 (0)