github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/compiler/consts.go (about) 1 // Copyright 2017 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 4 package compiler 5 6 import ( 7 "fmt" 8 "sort" 9 "strings" 10 11 "github.com/google/syzkaller/pkg/ast" 12 "github.com/google/syzkaller/prog" 13 "github.com/google/syzkaller/sys/targets" 14 ) 15 16 type ConstInfo struct { 17 Consts []*Const 18 Includes []string 19 Incdirs []string 20 Defines map[string]string 21 } 22 23 type Const struct { 24 Name string 25 Pos ast.Pos 26 } 27 28 func ExtractConsts(desc *ast.Description, target *targets.Target, eh ast.ErrorHandler) map[string]*ConstInfo { 29 res := Compile(desc, nil, target, eh) 30 if res == nil { 31 return nil 32 } 33 return res.fileConsts 34 } 35 36 // FabricateSyscallConsts adds syscall number constants to consts map. 37 // Used for test OS to not bother specifying consts for all syscalls. 38 func FabricateSyscallConsts(target *targets.Target, constInfo map[string]*ConstInfo, cf *ConstFile) { 39 if !target.SyscallNumbers { 40 return 41 } 42 for _, info := range constInfo { 43 for _, c := range info.Consts { 44 if strings.HasPrefix(c.Name, target.SyscallPrefix) { 45 cf.addConst(target.Arch, c.Name, 0, true) 46 } 47 } 48 } 49 } 50 51 // extractConsts returns list of literal constants and other info required for const value extraction. 52 func (comp *compiler) extractConsts() map[string]*ConstInfo { 53 infos := make(map[string]*constInfo) 54 for _, decl := range comp.desc.Nodes { 55 pos, _, _ := decl.Info() 56 info := getConstInfo(infos, pos) 57 switch n := decl.(type) { 58 case *ast.Include: 59 info.includeArray = append(info.includeArray, n.File.Value) 60 case *ast.Incdir: 61 info.incdirArray = append(info.incdirArray, n.Dir.Value) 62 case *ast.Define: 63 v := fmt.Sprint(n.Value.Value) 64 switch { 65 case n.Value.CExpr != "": 66 v = n.Value.CExpr 67 case n.Value.Ident != "": 68 v = n.Value.Ident 69 } 70 name := n.Name.Name 71 if _, builtin := comp.builtinConsts[name]; builtin { 72 comp.error(pos, "redefining builtin const %v", name) 73 } 74 info.defines[name] = v 75 comp.addConst(infos, pos, name) 76 case *ast.Call: 77 if comp.target.HasCallNumber(n.CallName) { 78 comp.addConst(infos, pos, comp.target.SyscallPrefix+n.CallName) 79 } 80 for _, attr := range n.Attrs { 81 if callAttrs[attr.Ident].Type == intAttr { 82 comp.addConst(infos, attr.Pos, attr.Args[0].Ident) 83 } 84 } 85 case *ast.Struct: 86 for _, attr := range n.Attrs { 87 attrDesc := structOrUnionAttrs(n)[attr.Ident] 88 if attrDesc.Type == intAttr { 89 comp.addConst(infos, attr.Pos, attr.Args[0].Ident) 90 } 91 } 92 foreachFieldAttrConst(n, func(t *ast.Type) { 93 comp.addConst(infos, t.Pos, t.Ident) 94 }) 95 } 96 switch decl.(type) { 97 case *ast.Call, *ast.Struct, *ast.Resource, *ast.TypeDef: 98 comp.extractTypeConsts(infos, decl) 99 } 100 } 101 comp.desc.Walk(ast.Recursive(func(n0 ast.Node) bool { 102 if n, ok := n0.(*ast.Int); ok { 103 comp.addConst(infos, n.Pos, n.Ident) 104 } 105 return true 106 })) 107 return convertConstInfo(infos, comp.fileMeta) 108 } 109 110 func foreachFieldAttrConst(n *ast.Struct, cb func(*ast.Type)) { 111 for _, field := range n.Fields { 112 for _, attr := range field.Attrs { 113 attrDesc := structOrUnionFieldAttrs(n)[attr.Ident] 114 if attrDesc == nil { 115 return 116 } 117 if attrDesc.Type != exprAttr { 118 // For now, only these field attrs may have consts. 119 return 120 } 121 ast.Recursive(func(n ast.Node) bool { 122 t, ok := n.(*ast.Type) 123 if !ok || t.Expression != nil { 124 return true 125 } 126 if t.Ident != valueIdent { 127 cb(t) 128 } 129 return false 130 })(attr.Args[0]) 131 } 132 } 133 } 134 135 func (comp *compiler) extractTypeConsts(infos map[string]*constInfo, n ast.Node) { 136 comp.foreachType(n, func(t *ast.Type, desc *typeDesc, args []*ast.Type, _ prog.IntTypeCommon) { 137 for i, arg := range args { 138 if desc.Args[i].Type.Kind&kindInt != 0 { 139 if arg.Ident != "" { 140 comp.addConst(infos, arg.Pos, arg.Ident) 141 } 142 for _, col := range arg.Colon { 143 if col.Ident != "" { 144 comp.addConst(infos, col.Pos, col.Ident) 145 } 146 } 147 } 148 } 149 }) 150 } 151 152 func (comp *compiler) addConst(infos map[string]*constInfo, pos ast.Pos, name string) { 153 if _, builtin := comp.builtinConsts[name]; builtin { 154 return 155 } 156 // In case of intN[identA], identA may refer to a constant or to a set of 157 // flags. To avoid marking all flags as constants, we must check here 158 // whether identA refers to a flag. We have a check in the compiler to 159 // ensure an identifier can never refer to both a constant and flags. 160 if _, isFlag := comp.intFlags[name]; isFlag { 161 return 162 } 163 info := getConstInfo(infos, pos) 164 info.consts[name] = &Const{ 165 Pos: pos, 166 Name: name, 167 } 168 } 169 170 type constInfo struct { 171 consts map[string]*Const 172 defines map[string]string 173 includeArray []string 174 incdirArray []string 175 } 176 177 func getConstInfo(infos map[string]*constInfo, pos ast.Pos) *constInfo { 178 info := infos[pos.File] 179 if info == nil { 180 info = &constInfo{ 181 consts: make(map[string]*Const), 182 defines: make(map[string]string), 183 } 184 infos[pos.File] = info 185 } 186 return info 187 } 188 189 func convertConstInfo(infos map[string]*constInfo, metas map[string]Meta) map[string]*ConstInfo { 190 res := make(map[string]*ConstInfo) 191 for file, info := range infos { 192 if file == ast.BuiltinFile { 193 continue 194 } 195 var allConsts []*Const 196 for name, val := range info.consts { 197 if name == "" { 198 continue 199 } 200 allConsts = append(allConsts, val) 201 } 202 sort.Slice(allConsts, func(i, j int) bool { 203 return allConsts[i].Name < allConsts[j].Name 204 }) 205 res[file] = &ConstInfo{ 206 Consts: allConsts, 207 Includes: info.includeArray, 208 Incdirs: info.incdirArray, 209 Defines: info.defines, 210 } 211 } 212 return res 213 } 214 215 // assignSyscallNumbers assigns syscall numbers, discards unsupported syscalls. 216 func (comp *compiler) assignSyscallNumbers(consts map[string]uint64) { 217 for _, decl := range comp.desc.Nodes { 218 c, ok := decl.(*ast.Call) 219 if !ok || strings.HasPrefix(c.CallName, "syz_") { 220 continue 221 } 222 str := comp.target.SyscallPrefix + c.CallName 223 nr, ok := consts[str] 224 if ok { 225 c.NR = nr 226 continue 227 } 228 c.NR = ^uint64(0) // mark as unused to not generate it 229 name := "syscall " + c.CallName 230 if !comp.unsupported[name] { 231 comp.unsupported[name] = true 232 comp.warning(c.Pos, "unsupported syscall: %v due to missing const %v", 233 c.CallName, str) 234 } 235 } 236 } 237 238 // patchConsts replaces all symbolic consts with their numeric values taken from consts map. 239 // Updates desc and returns set of unsupported syscalls and flags. 240 func (comp *compiler) patchConsts(consts0 map[string]uint64) { 241 consts := make(map[string]uint64) 242 for name, val := range consts0 { 243 consts[name] = val 244 } 245 for name, val := range comp.builtinConsts { 246 if _, ok := consts[name]; ok { 247 panic(fmt.Sprintf("builtin const %v already defined", name)) 248 } 249 consts[name] = val 250 } 251 for _, decl := range comp.desc.Nodes { 252 switch n := decl.(type) { 253 case *ast.IntFlags: 254 // Unsupported flag values are dropped. 255 var values []*ast.Int 256 for _, v := range n.Values { 257 if comp.patchIntConst(v, consts, nil) { 258 values = append(values, v) 259 } 260 } 261 n.Values = values 262 case *ast.Resource, *ast.Struct, *ast.Call, *ast.TypeDef: 263 // Walk whole tree and replace consts in Type's and Int's. 264 missing := "" 265 comp.foreachType(decl, func(_ *ast.Type, desc *typeDesc, 266 args []*ast.Type, _ prog.IntTypeCommon) { 267 for i, arg := range args { 268 if desc.Args[i].Type.Kind&kindInt != 0 { 269 comp.patchTypeConst(arg, consts, &missing) 270 } 271 } 272 }) 273 switch n := decl.(type) { 274 case *ast.Resource: 275 for _, v := range n.Values { 276 comp.patchIntConst(v, consts, &missing) 277 } 278 case *ast.Call: 279 for _, attr := range n.Attrs { 280 if callAttrs[attr.Ident].Type == intAttr { 281 comp.patchTypeConst(attr.Args[0], consts, &missing) 282 } 283 } 284 case *ast.Struct: 285 for _, attr := range n.Attrs { 286 attrDesc := structOrUnionAttrs(n)[attr.Ident] 287 if attrDesc.Type == intAttr { 288 comp.patchTypeConst(attr.Args[0], consts, &missing) 289 } 290 } 291 foreachFieldAttrConst(n, func(t *ast.Type) { 292 comp.patchTypeConst(t, consts, &missing) 293 }) 294 } 295 if missing == "" { 296 continue 297 } 298 // Produce a warning about unsupported syscall/resource/struct. 299 // TODO(dvyukov): we should transitively remove everything that 300 // depends on unsupported things. Potentially we still can get, 301 // say, a bad int range error due to the wrong const value. 302 // However, if we have a union where one of the options is 303 // arch-specific and does not have a const value, it's probably 304 // better to remove just that option. But then if we get to 0 305 // options in the union, we still need to remove it entirely. 306 pos, typ, name := decl.Info() 307 if id := typ + " " + name; !comp.unsupported[id] { 308 comp.unsupported[id] = true 309 comp.warning(pos, "unsupported %v: %v due to missing const %v", 310 typ, name, missing) 311 } 312 if c, ok := decl.(*ast.Call); ok { 313 c.NR = ^uint64(0) // mark as unused to not generate it 314 } 315 } 316 } 317 } 318 319 func (comp *compiler) patchIntConst(n *ast.Int, consts map[string]uint64, missing *string) bool { 320 return comp.patchConst(&n.Value, &n.Ident, consts, missing, false) 321 } 322 323 func (comp *compiler) patchTypeConst(n *ast.Type, consts map[string]uint64, missing *string) { 324 comp.patchConst(&n.Value, &n.Ident, consts, missing, true) 325 for _, col := range n.Colon { 326 comp.patchConst(&col.Value, &col.Ident, consts, missing, true) 327 } 328 } 329 330 func (comp *compiler) patchConst(val *uint64, id *string, consts map[string]uint64, missing *string, reset bool) bool { 331 if *id == "" { 332 return true 333 } 334 if v, ok := consts[*id]; ok { 335 if reset { 336 *id = "" 337 } 338 *val = v 339 return true 340 } 341 // This check is necessary because in intN[identA], identA may be a 342 // constant or a set of flags. 343 if _, isFlag := comp.intFlags[*id]; isFlag { 344 return true 345 } 346 if missing != nil && *missing == "" { 347 *missing = *id 348 } 349 // 1 is slightly safer than 0 and allows to work-around e.g. an array size 350 // that comes from a const missing on an arch. Also see the TODO in patchConsts. 351 *val = 1 352 return false 353 }