Skip to content

Commit 357cc31

Browse files
leonbettv8-internal-scoped@luci-project-accounts.iam.gserviceaccount.com
authored andcommitted
[wasm] Support globals with index types
This CL adds support for globals with index types. Bug: 533316073 Change-Id: I417c6eee3034590100a1c833c27038b5b866655b Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9529335 Commit-Queue: Leon Bettscheider <bettscheider@chromium.org> Reviewed-by: Matthias Liedtke <mliedtke@google.com>
1 parent fc15f6f commit 357cc31

10 files changed

Lines changed: 259 additions & 68 deletions

File tree

‎Sources/Fuzzilli/Base/ProgramBuilder.swift‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6268,8 +6268,13 @@ public class ProgramBuilder {
62686268
}
62696269

62706270
@discardableResult
6271-
public func addGlobal(wasmGlobal: WasmGlobal, isMutable: Bool) -> Variable {
6272-
return b.emit(WasmDefineGlobal(wasmGlobal: wasmGlobal, isMutable: isMutable)).output
6271+
public func addGlobal(wasmGlobal: WasmGlobal, isMutable: Bool, typeDef: Variable? = nil)
6272+
-> Variable
6273+
{
6274+
let inputs = typeDef != nil ? [typeDef!] : []
6275+
return b.emit(
6276+
WasmDefineGlobal(wasmGlobal: wasmGlobal, isMutable: isMutable), withInputs: inputs
6277+
).output
62736278
}
62746279

62756280
@discardableResult

‎Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ public let codeGeneratorWeights = [
287287
"WasmMemoryFillGenerator": 5,
288288
"WasmMemoryInitGenerator": 5,
289289
"WasmDefineGlobalGenerator": 2,
290+
"WasmDefineIndexTypedGlobalGenerator": 2,
290291
"WasmDefineTableGenerator": 2,
291292
// TODO(427115604): update onece both init and copy instructions are implemented.
292293
"WasmDefineElementSegmentGenerator": 1,

‎Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,15 @@ public let WasmCodeGenerators: [CodeGenerator] = [
850850
module.addGlobal(wasmGlobal: wasmGlobal, isMutable: probability(0.5))
851851
},
852852

853+
CodeGenerator(
854+
"WasmDefineIndexTypedGlobalGenerator", inContext: .single(.wasm),
855+
inputs: .required(.wasmTypeDef()),
856+
produces: [.object(ofGroup: "WasmGlobal")]
857+
) { b, type in
858+
let module = b.currentWasmModule
859+
module.addGlobal(wasmGlobal: .indexRef, isMutable: probability(0.5), typeDef: type)
860+
},
861+
853862
CodeGenerator(
854863
"WasmDefineTableGenerator", inContext: .single(.wasm),
855864
produces: [.object(ofGroup: "WasmTable")]

‎Sources/Fuzzilli/FuzzIL/Instruction.swift‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,9 @@ extension Instruction: ProtobufConvertible {
528528
case .imported(let ilType):
529529
return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.imported(
530530
ILTypeToWasmTypeEnum(ilType))
531+
case .indexRef:
532+
return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.nullref(
533+
Fuzzilli_Protobuf_WasmReferenceTypeKind.index)
531534
}
532535
}
533536

@@ -2047,6 +2050,8 @@ extension Instruction: ProtobufConvertible {
20472050
return .exnref
20482051
case .i31Ref:
20492052
return .i31ref
2053+
case .index:
2054+
return .indexRef
20502055
default:
20512056
fatalError("Unrecognized global wasm reference type \(val)")
20522057
}

‎Sources/Fuzzilli/FuzzIL/JSTyper.swift‎

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -760,14 +760,17 @@ public struct JSTyper: Analyzer {
760760
to: type(of: typeDef).wasmTypeDefinition!.getReferenceTypeTo(nullability: nullability))
761761
}
762762

763+
func getTypeDescription(of type: ILType) -> WasmTypeDescription {
764+
if case .Index(let desc) = type.wasmReferenceType?.kind {
765+
return desc.get()!
766+
}
767+
return type.wasmTypeDefinition!.description!
768+
}
769+
763770
// Returns the type description for the provided variable which has to be either a type
764771
// definition or an instance (wasm reference) of the wasm type.
765772
func getTypeDescription(of variable: Variable) -> WasmTypeDescription {
766-
let varType = type(of: variable)
767-
if case .Index(let desc) = varType.wasmReferenceType?.kind {
768-
return desc.get()!
769-
}
770-
return varType.wasmTypeDefinition!.description!
773+
return getTypeDescription(of: type(of: variable))
771774
}
772775

773776
// Helper function to type a "regular" wasm begin block (block, if, try).
@@ -882,10 +885,17 @@ public struct JSTyper: Analyzer {
882885
case .wasmSimdExtractLane(let op):
883886
setType(of: instr.output, to: op.kind.laneType())
884887
case .wasmDefineGlobal(let op):
888+
let valueType: ILType
889+
if case .indexRef = op.wasmGlobal {
890+
valueType = type(of: instr.input(0)).wasmTypeDefinition!.getReferenceTypeTo(
891+
nullability: true)
892+
} else {
893+
valueType = op.wasmGlobal.toType()
894+
}
885895
let type = ILType.object(
886896
ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"],
887897
withWasmType: WasmGlobalType(
888-
valueType: op.wasmGlobal.toType(), isMutable: op.isMutable))
898+
valueType: valueType, isMutable: op.isMutable))
889899
dynamicObjectGroupManager.addWasmGlobal(
890900
withType: type, forDefinition: instr, forVariable: instr.output)
891901
setType(of: instr.output, to: type)
@@ -949,10 +959,15 @@ public struct JSTyper: Analyzer {
949959
forVariable: instr.input(0))
950960
case .wasmLoadGlobal(let op):
951961
let definingInstruction = defUseAnalyzer.definition(of: instr.input(0))
962+
let globalVarType = type(of: instr.input(0))
952963
dynamicObjectGroupManager.addWasmGlobal(
953-
withType: type(of: instr.input(0)), forDefinition: definingInstruction,
964+
withType: globalVarType, forDefinition: definingInstruction,
954965
forVariable: instr.input(0))
955-
setType(of: instr.output, to: op.globalType)
966+
if let wasmGlobalType = globalVarType.wasmGlobalType {
967+
setType(of: instr.output, to: wasmGlobalType.valueType)
968+
} else {
969+
setType(of: instr.output, to: op.globalType)
970+
}
956971
case .wasmStoreGlobal(_):
957972
let definingInstruction = defUseAnalyzer.definition(of: instr.input(0))
958973
dynamicObjectGroupManager.addWasmGlobal(

‎Sources/Fuzzilli/FuzzIL/WasmOperations.swift‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,9 @@ public enum WasmGlobal {
743743
// function reference
744744
case refFunc(Int)
745745

746+
// Globals with index types. The specific index is passed separately to the instruction.
747+
case indexRef
748+
746749
// This is the case for imported Globals, we just need the type here.
747750
case imported(ILType)
748751

@@ -762,6 +765,10 @@ public enum WasmGlobal {
762765
return .wasmExnRef()
763766
case .i31ref:
764767
return .wasmI31Ref()
768+
case .indexRef:
769+
// The specific type definition is available as an input to the defining instruction.
770+
// Here we just return the generic representation so that we know it's a reference type.
771+
return .anyIndexRef
765772
case .imported(let type):
766773
assert(type.wasmGlobalType != nil)
767774
return type.wasmGlobalType!.valueType
@@ -805,7 +812,8 @@ public enum WasmGlobal {
805812
case .externref:
806813
return ""
807814
case .exnref,
808-
.i31ref:
815+
.i31ref,
816+
.indexRef:
809817
return "null"
810818
default:
811819
fatalError("Unimplemented / unhandled")
@@ -823,7 +831,9 @@ final class WasmDefineGlobal: WasmOperation {
823831
init(wasmGlobal: WasmGlobal, isMutable: Bool) {
824832
self.wasmGlobal = wasmGlobal
825833
self.isMutable = isMutable
826-
super.init(numOutputs: 1, attributes: [.isMutable], requiredContext: [.wasm])
834+
let numInputs = if case .indexRef = wasmGlobal { 1 } else { 0 }
835+
super.init(
836+
numInputs: numInputs, numOutputs: 1, attributes: [.isMutable], requiredContext: [.wasm])
827837
}
828838
}
829839

‎Sources/Fuzzilli/Lifting/FuzzILLifter.swift‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -975,7 +975,12 @@ public class FuzzILLifter: Lifter {
975975
w.emit("\(output()) <- EndWasmFunction \(inputs)")
976976

977977
case .wasmDefineGlobal(let op):
978-
w.emit("\(output()) <- WasmDefineGlobal \(op.wasmGlobal)")
978+
let isMutable = op.isMutable ? ", mutable" : ""
979+
if case .indexRef = op.wasmGlobal {
980+
w.emit("\(output()) <- WasmDefineGlobal (ref null \(input(0)))\(isMutable)")
981+
} else {
982+
w.emit("\(output()) <- WasmDefineGlobal \(op.wasmGlobal)\(isMutable)")
983+
}
979984

980985
case .wasmDefineTable(let op):
981986
let inputs = instr.inputs.map(lift).joined(separator: ", ")

‎Sources/Fuzzilli/Lifting/WasmLifter.swift‎

Lines changed: 70 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,38 +1125,52 @@ public class WasmLifter {
11251125

11261126
// TODO: in the future this should maybe be a context that allows instructions? Such that we can fuzz this expression as well?
11271127
for case .global(let instruction) in self.exports {
1128-
let definition = instruction!.op as! WasmDefineGlobal
1129-
let global = definition.wasmGlobal
1130-
1131-
temp += try encodeType(global.toType())
1132-
temp += Data([definition.isMutable ? 0x1 : 0x0])
1133-
// This has to be a constant expression: https://webassembly.github.io/spec/core/valid/instructions.html#constant-expressions
1134-
var temporaryInstruction: Instruction? = nil
1135-
// Also create some temporary output variables that do not have a number, these are only to satisfy the instruction assertions, maybe this can be done more nicely somehow.
1136-
switch global {
1137-
case .wasmf32(let val):
1138-
temporaryInstruction = Instruction(Constf32(value: val), output: Variable())
1139-
case .wasmf64(let val):
1140-
temporaryInstruction = Instruction(Constf64(value: val), output: Variable())
1141-
case .wasmi32(let val):
1142-
temporaryInstruction = Instruction(Consti32(value: val), output: Variable())
1143-
case .wasmi64(let val):
1144-
temporaryInstruction = Instruction(Consti64(value: val), output: Variable())
1145-
case .externref:
1146-
temp += try! Data([0xD0]) + encodeHeapType(.wasmExternRef()) + Data([0x0B])
1147-
continue
1148-
case .exnref:
1149-
temp += try! Data([0xD0]) + encodeHeapType(.wasmExnRef()) + Data([0x0B])
1150-
continue
1151-
case .i31ref:
1152-
temp += try! Data([0xD0]) + encodeHeapType(.wasmI31Ref()) + Data([0x0B])
1153-
continue
1154-
case .refFunc(_),
1155-
.imported(_):
1156-
fatalError("unreachable")
1128+
if let definition = instruction!.op as? WasmDefineGlobal {
1129+
if case .indexRef = definition.wasmGlobal {
1130+
let typeVar = instruction!.input(0)
1131+
let typeDesc = typer.getTypeDescription(of: typeVar)
1132+
let typeIndexData = try encodeWasmGCType(typeDesc)
1133+
1134+
// Type: (ref null $typeIndex). 0x63 is nullable ref.
1135+
temp += Data([0x63]) + typeIndexData
1136+
temp += Data([definition.isMutable ? 0x1 : 0x0])
1137+
1138+
// Init expr: ref.null $typeIndex, then end (0x0B)
1139+
temp += Data([0xD0]) + typeIndexData + Data([0x0B])
1140+
} else {
1141+
let global = definition.wasmGlobal
1142+
temp += try encodeType(global.toType())
1143+
temp += Data([definition.isMutable ? 0x1 : 0x0])
1144+
// This has to be a constant expression: https://webassembly.github.io/spec/core/valid/instructions.html#constant-expressions
1145+
var temporaryInstruction: Instruction? = nil
1146+
// Also create some temporary output variables that do not have a number, these are only to satisfy the instruction assertions, maybe this can be done more nicely somehow.
1147+
switch global {
1148+
case .wasmf32(let val):
1149+
temporaryInstruction = Instruction(Constf32(value: val), output: Variable())
1150+
case .wasmf64(let val):
1151+
temporaryInstruction = Instruction(Constf64(value: val), output: Variable())
1152+
case .wasmi32(let val):
1153+
temporaryInstruction = Instruction(Consti32(value: val), output: Variable())
1154+
case .wasmi64(let val):
1155+
temporaryInstruction = Instruction(Consti64(value: val), output: Variable())
1156+
case .externref:
1157+
temp += try! Data([0xD0]) + encodeHeapType(.wasmExternRef()) + Data([0x0B])
1158+
continue
1159+
case .exnref:
1160+
temp += try! Data([0xD0]) + encodeHeapType(.wasmExnRef()) + Data([0x0B])
1161+
continue
1162+
case .i31ref:
1163+
temp += try! Data([0xD0]) + encodeHeapType(.wasmI31Ref()) + Data([0x0B])
1164+
continue
1165+
case .refFunc(_),
1166+
.imported(_),
1167+
.indexRef:
1168+
fatalError("unreachable")
1169+
}
1170+
temp += try lift(temporaryInstruction!)
1171+
temp += Data([0x0B])
1172+
}
11571173
}
1158-
temp += try lift(temporaryInstruction!)
1159-
temp += Data([0x0B])
11601174
}
11611175

11621176
// Append the length of the section and the section contents itself.
@@ -1543,20 +1557,24 @@ public class WasmLifter {
15431557
// TODO: check if this is still accurate as we now only have defined imports.
15441558
// Also, we export the globals in the order we "see" them, which might mismatch the order in which they are laid out in the binary at the end, which is why we track the order of the globals separately.
15451559
private func importAnalysis() throws {
1560+
func registerTypeDependencies(for typeDesc: WasmTypeDescription) throws {
1561+
guard typeDesc.typeGroupIndex != -1 else {
1562+
throw CompileError.fatalError("Missing type group index for \(typeDesc)")
1563+
}
1564+
// Add typegroups and their dependencies.
1565+
if typeGroups.insert(typeDesc.typeGroupIndex).inserted {
1566+
typeGroups.formUnion(
1567+
typer.getTypeGroupDependencies(typeGroupIndex: typeDesc.typeGroupIndex))
1568+
}
1569+
}
1570+
15461571
for instr in self.instructionBuffer {
15471572
for (idx, input) in instr.inputs.enumerated() {
15481573
let inputType = typer.type(of: input)
15491574

15501575
if inputType.Is(.wasmTypeDef()) || inputType.Is(.anyIndexRef) {
15511576
let typeDesc = typer.getTypeDescription(of: input)
1552-
guard typeDesc.typeGroupIndex != -1 else {
1553-
throw CompileError.fatalError("Missing type group index for \(input)")
1554-
}
1555-
// Add typegroups and their dependencies.
1556-
if typeGroups.insert(typeDesc.typeGroupIndex).inserted {
1557-
typeGroups.formUnion(
1558-
typer.getTypeGroupDependencies(typeGroupIndex: typeDesc.typeGroupIndex))
1559-
}
1577+
try registerTypeDependencies(for: typeDesc)
15601578
}
15611579

15621580
for importType in [Export.table(nil), Export.memory(nil), Export.global(nil)] {
@@ -1565,6 +1583,19 @@ public class WasmLifter {
15651583
.import(
15661584
type: importType, variable: input, signature: nil, signatureDef: nil
15671585
))
1586+
1587+
// If the imported object uses a custom index type, ensure it's registered.
1588+
var valueType: ILType? = nil
1589+
if let globalType = inputType.wasmGlobalType {
1590+
valueType = globalType.valueType
1591+
} else if let tableType = inputType.wasmTableType {
1592+
valueType = tableType.elementType
1593+
}
1594+
1595+
if let valueType = valueType, valueType.Is(.anyIndexRef) {
1596+
let typeDesc = typer.getTypeDescription(of: valueType)
1597+
try registerTypeDependencies(for: typeDesc)
1598+
}
15681599
}
15691600
}
15701601

‎Sources/Fuzzilli/Mutators/OperationMutator.swift‎

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,8 @@ public class OperationMutator: BaseInstructionMutator {
324324
.imported:
325325
// TODO(cffsmith): Support these enum values or drop them from the WasmGlobal.
326326
fatalError("unimplemented")
327+
case .indexRef:
328+
fatalError("JS globals cannot have Wasm index types")
327329
}
328330
newOp = CreateWasmGlobal(value: wasmGlobal, isMutable: probability(0.5))
329331
case .createWasmMemory(let op):
@@ -433,22 +435,27 @@ public class OperationMutator: BaseInstructionMutator {
433435

434436
case .wasmDefineGlobal(let op):
435437
// We never change the type of the global, only the value as changing the type will break the following code pretty much instantly.
436-
let wasmGlobal: WasmGlobal =
437-
switch op.wasmGlobal.toType() {
438-
case .wasmf32:
439-
.wasmf32(Float32(b.randomFloat()))
440-
case .wasmf64:
441-
.wasmf64(b.randomFloat())
442-
case .wasmi32:
443-
.wasmi32(Int32(truncatingIfNeeded: b.randomInt()))
444-
case .wasmi64:
445-
.wasmi64(b.randomInt())
446-
case ILType.wasmExternRef(), ILType.wasmExnRef(), ILType.wasmI31Ref():
447-
op.wasmGlobal
448-
default:
449-
fatalError("unexpected/unimplemented Value Type!")
450-
}
451-
newOp = WasmDefineGlobal(wasmGlobal: wasmGlobal, isMutable: probability(0.5))
438+
if case .indexRef = op.wasmGlobal {
439+
newOp = WasmDefineGlobal(wasmGlobal: .indexRef, isMutable: probability(0.5))
440+
} else {
441+
let oldWasmGlobal = op.wasmGlobal
442+
let wasmGlobal: WasmGlobal =
443+
switch oldWasmGlobal.toType() {
444+
case .wasmf32:
445+
.wasmf32(Float32(b.randomFloat()))
446+
case .wasmf64:
447+
.wasmf64(b.randomFloat())
448+
case .wasmi32:
449+
.wasmi32(Int32(truncatingIfNeeded: b.randomInt()))
450+
case .wasmi64:
451+
.wasmi64(b.randomInt())
452+
case ILType.wasmExternRef(), ILType.wasmExnRef(), ILType.wasmI31Ref():
453+
oldWasmGlobal
454+
default:
455+
fatalError("unexpected/unimplemented Value Type!")
456+
}
457+
newOp = WasmDefineGlobal(wasmGlobal: wasmGlobal, isMutable: probability(0.5))
458+
}
452459
case .wasmDefineTable(let op):
453460
// TODO: change table size?
454461
newOp = op

0 commit comments

Comments
 (0)