github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/kfuzztest/builder.go (about) 1 // Copyright 2025 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 package kfuzztest 4 5 import ( 6 "debug/dwarf" 7 "fmt" 8 "strings" 9 10 "github.com/google/syzkaller/pkg/ast" 11 ) 12 13 type Builder struct { 14 funcs []SyzFunc 15 structs []SyzStruct 16 constraints []SyzConstraint 17 annotations []SyzAnnotation 18 } 19 20 func NewBuilder( 21 funcs []SyzFunc, 22 structs []SyzStruct, 23 constraints []SyzConstraint, 24 annotations []SyzAnnotation, 25 ) *Builder { 26 return &Builder{funcs, structs, constraints, annotations} 27 } 28 29 func (b *Builder) AddStruct(s SyzStruct) { 30 b.structs = append(b.structs, s) 31 } 32 33 func (b *Builder) AddFunc(f SyzFunc) { 34 b.funcs = append(b.funcs, f) 35 } 36 37 func (b *Builder) EmitSyzlangDescription() (string, error) { 38 constraintMap := make(map[string]map[string]SyzConstraint) 39 for _, constraint := range b.constraints { 40 if _, contains := constraintMap[constraint.InputType]; !contains { 41 constraintMap[constraint.InputType] = make(map[string]SyzConstraint) 42 } 43 constraintMap[constraint.InputType][constraint.FieldName] = constraint 44 } 45 annotationMap := make(map[string]map[string]SyzAnnotation) 46 for _, annotation := range b.annotations { 47 if _, contains := annotationMap[annotation.InputType]; !contains { 48 annotationMap[annotation.InputType] = make(map[string]SyzAnnotation) 49 } 50 annotationMap[annotation.InputType][annotation.FieldName] = annotation 51 } 52 53 var descBuilder strings.Builder 54 descBuilder.WriteString("# This description was automatically generated with tools/kfuzztest-gen\n") 55 for _, s := range b.structs { 56 structDesc, err := syzStructToSyzlang(s, constraintMap, annotationMap) 57 if err != nil { 58 return "", err 59 } 60 descBuilder.WriteString(structDesc) 61 descBuilder.WriteString("\n\n") 62 } 63 64 for i, fn := range b.funcs { 65 descBuilder.WriteString(syzFuncToSyzlang(fn)) 66 if i < len(b.funcs)-1 { 67 descBuilder.WriteString("\n") 68 } 69 } 70 71 // Format the output syzlang descriptions for consistency. 72 var astError error 73 eh := func(pos ast.Pos, msg string) { 74 astError = fmt.Errorf("ast failure: %v: %v", pos, msg) 75 } 76 descAst := ast.Parse([]byte(descBuilder.String()), "", eh) 77 if astError != nil { 78 return "", astError 79 } 80 if descAst == nil { 81 return "", fmt.Errorf("failed to format generated syzkaller description - is it well-formed?") 82 } 83 return string(ast.Format(descAst)), nil 84 } 85 86 func syzStructToSyzlang(s SyzStruct, constraintMap map[string]map[string]SyzConstraint, 87 annotationMap map[string]map[string]SyzAnnotation) (string, error) { 88 var builder strings.Builder 89 90 fmt.Fprintf(&builder, "%s {\n", s.Name) 91 structAnnotations := annotationMap["struct "+s.Name] 92 structConstraints := constraintMap["struct "+s.Name] 93 for _, field := range s.Fields { 94 line, err := syzFieldToSyzLang(field, structConstraints, structAnnotations) 95 if err != nil { 96 return "", err 97 } 98 fmt.Fprintf(&builder, "\t%s\n", line) 99 } 100 fmt.Fprint(&builder, "}") 101 return builder.String(), nil 102 } 103 104 func syzFieldToSyzLang(field SyzField, constraintMap map[string]SyzConstraint, 105 annotationMap map[string]SyzAnnotation) (string, error) { 106 constraint, hasConstraint := constraintMap[field.Name] 107 annotation, hasAnnotation := annotationMap[field.Name] 108 109 var typeDesc string 110 var err error 111 if hasAnnotation { 112 // Annotations override the existing type definitions. 113 typeDesc, err = processAnnotation(field, annotation) 114 } else { 115 typeDesc, err = dwarfToSyzlangType(field.dwarfType) 116 } 117 if err != nil { 118 return "", err 119 } 120 121 // Process constraints only if unannotated. 122 // TODO: is there a situation where we would want to process both? 123 if hasConstraint && !hasAnnotation { 124 constraint, err := processConstraint(constraint) 125 if err != nil { 126 return "", err 127 } 128 typeDesc += constraint 129 } 130 return fmt.Sprintf("%s %s", field.Name, typeDesc), nil 131 } 132 133 func processConstraint(c SyzConstraint) (string, error) { 134 switch c.ConstraintType { 135 case ExpectEq: 136 return fmt.Sprintf("[%d]", c.Value1), nil 137 case ExpectNe: 138 // syzkaller does not have a built-in way to support an inequality 139 // constraint AFAIK. 140 return "", nil 141 case ExpectLt: 142 return fmt.Sprintf("[0:%d]", c.Value1-1), nil 143 case ExpectLe: 144 return fmt.Sprintf("[0:%d]", c.Value1), nil 145 case ExpectGt: 146 return fmt.Sprintf("[%d]", c.Value1+1), nil 147 case ExpectGe: 148 return fmt.Sprintf("[%d]", c.Value1), nil 149 case ExpectInRange: 150 return fmt.Sprintf("[%d:%d]", c.Value1, c.Value2), nil 151 default: 152 fmt.Printf("c = %d\n", c.ConstraintType) 153 return "", fmt.Errorf("unsupported constraint type") 154 } 155 } 156 157 func processAnnotation(field SyzField, annotation SyzAnnotation) (string, error) { 158 switch annotation.Attribute { 159 case AttributeLen: 160 underlyingType, err := dwarfToSyzlangType(field.dwarfType) 161 if err != nil { 162 return "", err 163 } 164 return fmt.Sprintf("len[%s, %s]", annotation.LinkedFieldName, underlyingType), nil 165 case AttributeString: 166 return "ptr[in, string]", nil 167 case AttributeArray: 168 pointeeType, isPtr := resolvesToPtr(field.dwarfType) 169 if !isPtr { 170 return "", fmt.Errorf("can only annotate pointer fields are arrays") 171 } 172 // TODO: discards const qualifier. 173 typeDesc, err := dwarfToSyzlangType(pointeeType) 174 if err != nil { 175 return "", err 176 } 177 return fmt.Sprintf("ptr[in, array[%s]]", typeDesc), nil 178 default: 179 return "", fmt.Errorf("unsupported attribute type") 180 } 181 } 182 183 // Returns true iff `dwarfType` resolved down to a pointer. For example, 184 // a `const *void` which isn't directly a pointer. 185 func resolvesToPtr(dwarfType dwarf.Type) (dwarf.Type, bool) { 186 switch t := dwarfType.(type) { 187 case *dwarf.QualType: 188 return resolvesToPtr(t.Type) 189 case *dwarf.PtrType: 190 return t.Type, true 191 } 192 return nil, false 193 } 194 195 func syzFuncToSyzlang(s SyzFunc) string { 196 var builder strings.Builder 197 typeName := strings.TrimPrefix(s.InputStructName, "struct ") 198 199 fmt.Fprintf(&builder, "syz_kfuzztest_run$%s(", s.Name) 200 fmt.Fprintf(&builder, "name ptr[in, string[\"%s\"]], ", s.Name) 201 fmt.Fprintf(&builder, "data ptr[in, %s], ", typeName) 202 builder.WriteString("len bytesize[data], ") 203 builder.WriteString("buf ptr[in, array[int8, 65536]]) ") 204 // TODO:(ethangraham) The only other way I can think of getting this name 205 // would involve using the "reflect" package and matching against the 206 // KFuzzTest name, which isn't much better than hardcoding this. 207 builder.WriteString("(kfuzz_test)") 208 return builder.String() 209 } 210 211 // Given a dwarf type, returns a syzlang string representation of this type. 212 func dwarfToSyzlangType(dwarfType dwarf.Type) (string, error) { 213 switch t := dwarfType.(type) { 214 case *dwarf.PtrType: 215 underlyingType, err := dwarfToSyzlangType(t.Type) 216 if err != nil { 217 return "", err 218 } 219 return fmt.Sprintf("ptr[in, %s]", underlyingType), nil 220 case *dwarf.QualType: 221 if t.Qual == "const" { 222 return dwarfToSyzlangType(t.Type) 223 } else { 224 return "", fmt.Errorf("no support for %s qualifier", t.Qual) 225 } 226 case *dwarf.ArrayType: 227 underlyingType, err := dwarfToSyzlangType(t.Type) 228 if err != nil { 229 return "", err 230 } 231 // If t.Count == -1 then this is a varlen array as per debug/dwarf 232 // documentation. 233 if t.Count == -1 { 234 return fmt.Sprintf("array[%s]", underlyingType), nil 235 } else { 236 return fmt.Sprintf("array[%s, %d]", underlyingType, t.Count), nil 237 } 238 case *dwarf.TypedefType: 239 return dwarfToSyzlangType(t.Type) 240 case *dwarf.IntType, *dwarf.UintType: 241 numBits := t.Size() * 8 242 return fmt.Sprintf("int%d", numBits), nil 243 case *dwarf.CharType, *dwarf.UcharType: 244 return "int8", nil 245 // `void` isn't a valid type by itself, so we know that it would have 246 // been wrapped in a pointer, e.g., `void *`. For this reason, we can return 247 // just interpret it as a byte, i.e., int8. 248 case *dwarf.VoidType: 249 return "int8", nil 250 case *dwarf.StructType: 251 return strings.TrimPrefix(t.StructName, "struct "), nil 252 default: 253 return "", fmt.Errorf("unsupported type %s", dwarfType.String()) 254 } 255 }