github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/compiler/types.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 "bytes" 8 "encoding/binary" 9 "fmt" 10 "sort" 11 "strconv" 12 13 "github.com/google/syzkaller/pkg/ast" 14 "github.com/google/syzkaller/prog" 15 ) 16 17 // typeDesc is arg/field type descriptor. 18 type typeDesc struct { 19 Names []string 20 CanBeTypedef bool // can be type alias target? 21 CantBeOpt bool // can't be marked as opt? 22 CantBeOut bool // can't be used as an explicitly output argument? 23 CantHaveOut bool // everything inside can only be in 24 NeedBase bool // needs base type when used as field? 25 MaxColon int // max number of colons (int8:2) on fields 26 OptArgs int // number of optional arguments in Args array 27 Args []namedArg // type arguments 28 RequiresCallAttrs map[string]bool // calls using this type must have these attrs. 29 // CanBeArgRet returns if this type can be syscall argument/return (false if nil). 30 CanBeArgRet func(comp *compiler, t *ast.Type) (bool, bool) 31 // CanBeResourceBase returns if this type can be a resource base type (false if nil. 32 CanBeResourceBase func(comp *compiler, t *ast.Type) bool 33 // Check does custom verification of the type (optional, consts are not patched yet). 34 Check func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) 35 // CheckConsts does custom verification of the type (optional, consts are patched). 36 CheckConsts func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) 37 // Varlen returns if the type is variable-length (false if not set). 38 Varlen func(comp *compiler, t *ast.Type, args []*ast.Type) bool 39 // ZeroSize returns if the type has static 0 size (false if not set). 40 ZeroSize func(comp *compiler, t *ast.Type, args []*ast.Type) bool 41 // Gen generates corresponding prog.Type. 42 Gen func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type 43 } 44 45 // typeArg describes a type argument. 46 type typeArg struct { 47 Names []string 48 Kind int // int/ident/string 49 MaxArgs int // maxiumum number of subargs 50 MaxColon int // max number of colons (2:3:4) 51 // Check does custom verification of the arg (optional). 52 Check func(comp *compiler, t *ast.Type) 53 CheckConsts func(comp *compiler, t *ast.Type) 54 } 55 56 type namedArg struct { 57 Name string 58 Type *typeArg 59 IsArg bool // does not need base type 60 } 61 62 const ( 63 kindAny = 0 64 kindInt = 1 << iota 65 kindIdent 66 kindString 67 ) 68 69 func canBeArg(comp *compiler, t *ast.Type) (bool, bool) { return true, false } 70 func canBeArgRet(comp *compiler, t *ast.Type) (bool, bool) { return true, true } 71 72 var typeInt = &typeDesc{ 73 Names: typeArgBase.Type.Names, 74 CanBeArgRet: canBeArg, 75 CanBeTypedef: true, 76 MaxColon: 1, 77 OptArgs: 2, 78 Args: []namedArg{ 79 {Name: "value", Type: typeArgIntValue}, 80 {Name: "align", Type: typeArgIntAlign}, 81 }, 82 CanBeResourceBase: func(comp *compiler, t *ast.Type) bool { 83 // Big-endian resources can always be converted to non-big-endian, 84 // since we will always revert bytes during copyout and during copyin, 85 // so the result is the same as not reverting at all. 86 // Big-endian resources are also not implemented and don't have tests. 87 _, be := comp.parseIntType(t.Ident) 88 return !be 89 }, 90 Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 91 typeArgBase.Type.Check(comp, t) 92 if len(args) > 1 && len(args[0].Colon) == 0 { 93 comp.error(args[1].Pos, "align argument of %v is not supported unless first argument is a range", 94 t.Ident) 95 } 96 }, 97 CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 98 if len(args) > 0 && len(args[0].Colon) != 0 { 99 begin := args[0].Value 100 end := args[0].Colon[0].Value 101 size, _ := comp.parseIntType(t.Ident) 102 size = size * 8 103 if len(t.Colon) != 0 { 104 // Integer is bitfield. 105 size = t.Colon[0].Value 106 } 107 maxUInt := uint64(1<<size - 1) 108 maxSInt := uint64(1<<(size-1) - 1) 109 if len(args) > 1 && begin == 0 && int64(end) == -1 { 110 // intN[0:-1, align] is a special value for 'all possible values', but aligned. 111 end = maxUInt 112 } else if end-begin > 1<<64-1<<32 { 113 comp.error(args[0].Pos, "bad int range [%v:%v]", begin, end) 114 return 115 } 116 // The range fits into the size if treated as unsigned [0:MAX_UINT]. 117 inUnsignedBase := begin <= maxUInt && end <= maxUInt 118 // The range fits into the size if treated as signed [-MIN_SINT:MAX_SINT]. 119 inSignedBase := begin+maxSInt <= maxUInt && end+maxSInt <= maxUInt 120 if size < 64 && !inUnsignedBase && !inSignedBase { 121 comp.error(args[0].Colon[0].Pos, "int range [%v:%v] is too large for base type of size %v", 122 begin, end, size) 123 return 124 } 125 if len(args) > 1 && args[1].Value != 0 && (end-begin)/args[1].Value == 0 { 126 comp.error(args[1].Pos, "int alignment %v is too large for range [%v:%v]", 127 args[1].Value, begin, end) 128 } 129 } 130 }, 131 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 132 size, be := comp.parseIntType(t.Ident) 133 var bitLen uint64 134 if len(t.Colon) != 0 { 135 bitLen = t.Colon[0].Value 136 } 137 base.TypeSize = size 138 base.TypeAlign = getIntAlignment(comp, base) 139 base = genIntCommon(base.TypeCommon, bitLen, be) 140 141 kind, rangeBegin, rangeEnd, align := prog.IntPlain, uint64(0), uint64(0), uint64(0) 142 if len(args) > 0 { 143 rangeArg := args[0] 144 if _, isIntFlag := comp.intFlags[rangeArg.Ident]; isIntFlag { 145 return generateFlagsType(comp, base, rangeArg.Ident) 146 } 147 if len(rangeArg.Colon) == 0 { 148 // If we have an argument that is not a range, then it's a const. 149 return &prog.ConstType{ 150 IntTypeCommon: base, 151 Val: args[0].Value, 152 } 153 } 154 kind, rangeBegin, rangeEnd = prog.IntRange, rangeArg.Value, rangeArg.Colon[0].Value 155 if len(args) > 1 { 156 align = args[1].Value 157 } 158 } 159 return &prog.IntType{ 160 IntTypeCommon: base, 161 Kind: kind, 162 RangeBegin: rangeBegin, 163 RangeEnd: rangeEnd, 164 Align: align, 165 } 166 }, 167 } 168 169 func generateFlagsType(comp *compiler, base prog.IntTypeCommon, name string) prog.Type { 170 base.TypeName = name 171 f := comp.intFlags[name] 172 values := genIntArray(f.Values) 173 if len(values) == 0 || len(values) == 1 && values[0] == 0 { 174 // We can get this if all values are unsupported consts. 175 // Also generate const[0] if we have only 1 flags value which is 0, 176 // this is the intention in all existing cases (e.g. an enum with types 177 // of something, but there is really only 1 type exists). 178 return &prog.ConstType{ 179 IntTypeCommon: base, 180 Val: 0, 181 } 182 } 183 sort.Slice(values, func(i, j int) bool { 184 return values[i] < values[j] 185 }) 186 return &prog.FlagsType{ 187 IntTypeCommon: base, 188 Vals: values, 189 BitMask: isBitmask(values), 190 } 191 } 192 193 func getIntAlignment(comp *compiler, base prog.IntTypeCommon) uint64 { 194 align := base.UnitSize() 195 if align == 8 && comp.target.Int64Alignment != 0 { 196 align = comp.target.Int64Alignment 197 } 198 return align 199 } 200 201 var typePtr = &typeDesc{ 202 Names: []string{"ptr", "ptr64"}, 203 CanBeArgRet: canBeArg, 204 CantBeOut: true, 205 CanBeTypedef: true, 206 Args: []namedArg{{Name: "direction", Type: typeArgDir}, {Name: "type", Type: typeArgType}}, 207 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 208 base.TypeSize = comp.ptrSize 209 if t.Ident == "ptr64" { 210 base.TypeSize = 8 211 } 212 base.TypeAlign = getIntAlignment(comp, base) 213 elem := comp.genType(args[1], 0) 214 elemDir := genDir(args[0]) 215 return &prog.PtrType{ 216 TypeCommon: base.TypeCommon, 217 Elem: elem, 218 ElemDir: elemDir, 219 } 220 }, 221 } 222 223 func isSquashableElem(elem prog.Type, dir prog.Dir) bool { 224 if dir != prog.DirIn { 225 return false 226 } 227 // Check if the pointer element contains something that can be complex, and does not contain 228 // anything unsupported we don't want to sqaush. Prog package later checks at runtime 229 // if a concrete arg actually contains something complex. But we look at the whole type 230 // to understand if it contains anything unsupported b/c a union may contain e.g. a complex struct 231 // and a filename we don't want to squash, or an array may contain something unsupported, 232 // but has 0 size in a concrete argument. 233 complex, unsupported := false, false 234 prog.ForeachArgType(elem, func(t prog.Type, ctx *prog.TypeCtx) { 235 switch typ := t.(type) { 236 case *prog.StructType: 237 if typ.Varlen() { 238 complex = true 239 } 240 if typ.OverlayField != 0 { 241 // Squashing of structs with out_overlay is not supported. 242 // If we do it, we need to be careful to either squash out part as well, 243 // or remove any resources in the out part from the prog. 244 unsupported = true 245 } 246 case *prog.UnionType: 247 if typ.Varlen() && len(typ.Fields) > 5 { 248 complex = true 249 } 250 case *prog.PtrType: 251 // Squashing of pointers is not supported b/c if we do it 252 // we will pass random garbage as pointers. 253 unsupported = true 254 case *prog.BufferType: 255 switch typ.Kind { 256 case prog.BufferFilename, prog.BufferGlob, prog.BufferCompressed: 257 // Squashing file names may lead to unwanted escaping paths (e.g. "/"), 258 // squashing compressed buffers is not useful since we uncompress them ourselves 259 // (not the kernel). 260 unsupported = true 261 } 262 } 263 ctx.Stop = unsupported 264 }) 265 return complex && !unsupported 266 } 267 268 var typeVoid = &typeDesc{ 269 Names: []string{"void"}, 270 CantBeOpt: true, 271 ZeroSize: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 272 return true 273 }, 274 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 275 base.TypeSize = 0 // the only type with static size 0 276 base.TypeAlign = 1 277 return &prog.BufferType{ 278 TypeCommon: base.TypeCommon, 279 Kind: prog.BufferBlobRange, 280 RangeBegin: 0, 281 RangeEnd: 0, 282 } 283 }, 284 } 285 286 var typeArray = &typeDesc{ 287 Names: []string{"array"}, 288 CanBeTypedef: true, 289 CantBeOpt: true, 290 OptArgs: 1, 291 Args: []namedArg{{Name: "type", Type: typeArgType}, {Name: "size", Type: typeArgSizeRange}}, 292 CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 293 if len(args) > 1 && args[1].Value == 0 && (len(args[1].Colon) == 0 || args[1].Colon[0].Value == 0) { 294 comp.error(args[1].Pos, "arrays of size 0 are not supported") 295 } 296 }, 297 Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 298 if comp.isZeroSize(args[0]) { 299 return false 300 } 301 if comp.isVarlen(args[0]) { 302 return true 303 } 304 if len(args) > 1 { 305 return len(args[1].Colon) != 0 && args[1].Value != args[1].Colon[0].Value 306 } 307 return true 308 }, 309 ZeroSize: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 310 return comp.isZeroSize(args[0]) 311 }, 312 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 313 elemType := comp.genType(args[0], 0) 314 kind, begin, end := prog.ArrayRandLen, uint64(0), uint64(0) 315 if len(args) > 1 { 316 kind, begin, end = prog.ArrayRangeLen, args[1].Value, args[1].Value 317 if len(args[1].Colon) != 0 { 318 end = args[1].Colon[0].Value 319 } 320 } 321 if it, ok := elemType.(*prog.IntType); ok && it.Kind == prog.IntPlain && it.TypeSize == 1 { 322 // Special case: buffer is better mutated. 323 bufKind := prog.BufferBlobRand 324 base.TypeSize = 0 325 if kind == prog.ArrayRangeLen { 326 bufKind = prog.BufferBlobRange 327 if begin == end { 328 base.TypeSize = begin * elemType.Size() 329 } 330 } 331 base.TypeAlign = 1 332 return &prog.BufferType{ 333 TypeCommon: base.TypeCommon, 334 Kind: bufKind, 335 RangeBegin: begin, 336 RangeEnd: end, 337 } 338 } 339 if ct, ok := elemType.(*prog.ConstType); ok && 340 (ct.ArgFormat == prog.FormatNative || ct.ArgFormat == prog.FormatBigEndian) && 341 kind == prog.ArrayRangeLen && begin == end { 342 // Special case: const string takes less space in C programs. 343 base.TypeSize = begin * ct.Size() 344 base.TypeAlign = ct.TypeAlign 345 val := make([]byte, 8) 346 if ct.ArgFormat == prog.FormatBigEndian { 347 binary.BigEndian.PutUint64(val, ct.Val) 348 val = val[8-ct.Size():] 349 } else { 350 binary.LittleEndian.PutUint64(val, ct.Val) 351 val = val[:ct.Size()] 352 } 353 val = bytes.Repeat(val, int(begin)) 354 return &prog.BufferType{ 355 TypeCommon: base.TypeCommon, 356 Kind: prog.BufferString, 357 Values: []string{string(val)}, 358 NoZ: true, 359 } 360 } 361 // TypeSize is assigned later in layoutArray. 362 base.TypeAlign = elemType.Alignment() 363 return &prog.ArrayType{ 364 TypeCommon: base.TypeCommon, 365 Elem: elemType, 366 Kind: kind, 367 RangeBegin: begin, 368 RangeEnd: end, 369 } 370 }, 371 } 372 373 var typeLen = &typeDesc{ 374 Names: []string{"len", "bytesize", "bytesize2", "bytesize4", "bytesize8", "bitsize", "offsetof"}, 375 CanBeArgRet: canBeArg, 376 CantBeOpt: true, 377 CantBeOut: true, 378 NeedBase: true, 379 Args: []namedArg{{Name: "len target", Type: typeArgLenTarget}}, 380 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 381 var bitSize uint64 382 var offset bool 383 switch t.Ident { 384 case "bytesize": 385 bitSize = 8 386 case "bytesize2", "bytesize4", "bytesize8": 387 byteSize, _ := strconv.ParseUint(t.Ident[8:], 10, 8) 388 bitSize = byteSize * 8 389 case "bitsize": 390 bitSize = 1 391 case "offsetof": 392 bitSize = 8 393 offset = true 394 } 395 path := []string{args[0].Ident} 396 for _, col := range args[0].Colon { 397 path = append(path, col.Ident) 398 } 399 base.TypeAlign = getIntAlignment(comp, base) 400 return &prog.LenType{ 401 IntTypeCommon: base, 402 Path: path, 403 BitSize: bitSize, 404 Offset: offset, 405 } 406 }, 407 } 408 409 var typeConst = &typeDesc{ 410 Names: []string{"const"}, 411 CanBeArgRet: canBeArg, 412 CanBeTypedef: true, 413 CantBeOpt: true, 414 CantBeOut: true, 415 NeedBase: true, 416 Args: []namedArg{{Name: "value", Type: typeArgInt}}, 417 CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 418 v := args[0].Value 419 bitSize := base.TypeBitSize() 420 if constOverflowsBase(v, base) { 421 comp.error(args[0].Pos, "const val 0x%x does not fit into %v bits", v, bitSize) 422 } 423 args[0].Value = v & (uint64(1)<<bitSize - 1) 424 }, 425 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 426 base.TypeAlign = getIntAlignment(comp, base) 427 return &prog.ConstType{ 428 IntTypeCommon: base, 429 Val: args[0].Value, 430 } 431 }, 432 } 433 434 func constOverflowsBase(v uint64, base prog.IntTypeCommon) bool { 435 size := base.TypeBitSize() 436 if size == 64 { 437 return false 438 } 439 mask := uint64(1)<<size - 1 440 v1 := v & mask 441 if int64(v1<<(64-size)) < 0 && int64(v) < 0 { 442 v1 |= ^mask 443 } 444 return v1 != v 445 } 446 447 var typeArgLenTarget = &typeArg{ 448 Kind: kindIdent, 449 MaxColon: 10, 450 } 451 452 var typeFlags = &typeDesc{ 453 Names: []string{"flags"}, 454 CanBeArgRet: canBeArg, 455 CanBeTypedef: true, 456 CantBeOpt: true, 457 CantBeOut: true, 458 NeedBase: true, 459 Args: []namedArg{{Name: "flags", Type: typeArgFlags}}, 460 CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 461 name := args[0].Ident 462 if name == "xdp_mmap_offsets" && comp.ptrSize == 4 { 463 // TODO(dvyukov): this sucks a lot. It seems that out 32-bit mmap is wrong. 464 // The syscall accepts number of pages as int32, but we pass offset in bytes. 465 // As the result large XDP consts don't fit into the arg. 466 return 467 } 468 f := comp.intFlags[name] 469 for _, val := range f.Values { 470 if constOverflowsBase(val.Value, base) { 471 comp.error(args[0].Pos, "%v %v=0x%x doesn't fit into %v bits", 472 name, val.Ident, val.Value, base.TypeBitSize()) 473 } 474 } 475 }, 476 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 477 base.TypeAlign = getIntAlignment(comp, base) 478 return generateFlagsType(comp, base, args[0].Ident) 479 }, 480 } 481 482 func isBitmask(values []uint64) bool { 483 if values[0] == 0 { 484 // 0 can't be part of bitmask, this helps to handle important 485 // case like "0, 1" and "0, 1, 2" that would be detected 486 // as bitmask otherwise. 487 return false 488 } 489 var combined uint64 490 for _, v := range values { 491 if v&combined != 0 { 492 return false 493 } 494 combined |= v 495 } 496 return true 497 } 498 499 var typeArgFlags = &typeArg{ 500 Kind: kindIdent, 501 Check: func(comp *compiler, t *ast.Type) { 502 if comp.intFlags[t.Ident] == nil { 503 comp.error(t.Pos, "unknown flags %v", t.Ident) 504 return 505 } 506 }, 507 } 508 509 var typeVMA = &typeDesc{ 510 Names: []string{"vma", "vma64"}, 511 CanBeArgRet: canBeArg, 512 OptArgs: 1, 513 Args: []namedArg{{Name: "size range", Type: typeArgSizeRange}}, 514 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 515 var begin, end uint64 516 if len(args) > 0 { 517 begin, end = args[0].Value, args[0].Value 518 if len(args[0].Colon) != 0 { 519 end = args[0].Colon[0].Value 520 } 521 } 522 base.TypeSize = comp.ptrSize 523 if t.Ident == "vma64" { 524 base.TypeSize = 8 525 } 526 base.TypeAlign = getIntAlignment(comp, base) 527 return &prog.VmaType{ 528 TypeCommon: base.TypeCommon, 529 RangeBegin: begin, 530 RangeEnd: end, 531 } 532 }, 533 } 534 535 var typeCsum = &typeDesc{ 536 Names: []string{"csum"}, 537 NeedBase: true, 538 CantBeOpt: true, 539 CantBeOut: true, 540 OptArgs: 1, 541 Args: []namedArg{ 542 {Name: "csum target", Type: typeArgLenTarget}, 543 {Name: "kind", Type: typeArgCsumType}, 544 {Name: "proto", Type: typeArgInt}, 545 }, 546 Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 547 if len(args) > 2 && genCsumKind(args[1]) != prog.CsumPseudo { 548 comp.error(args[2].Pos, "only pseudo csum can have proto") 549 } 550 if len(args[0].Colon) != 0 { 551 comp.error(args[0].Colon[0].Pos, "path expressions are not implemented for csum") 552 } 553 }, 554 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 555 var proto uint64 556 if len(args) > 2 { 557 proto = args[2].Value 558 } 559 base.TypeAlign = getIntAlignment(comp, base) 560 return &prog.CsumType{ 561 IntTypeCommon: base, 562 Buf: args[0].Ident, 563 Kind: genCsumKind(args[1]), 564 Protocol: proto, 565 } 566 }, 567 } 568 569 var typeArgCsumType = &typeArg{ 570 Kind: kindIdent, 571 Names: []string{"inet", "pseudo"}, 572 } 573 574 func genCsumKind(t *ast.Type) prog.CsumKind { 575 switch t.Ident { 576 case "inet": 577 return prog.CsumInet 578 case "pseudo": 579 return prog.CsumPseudo 580 default: 581 panic(fmt.Sprintf("unknown csum kind %q", t.Ident)) 582 } 583 } 584 585 var typeProc = &typeDesc{ 586 Names: []string{"proc"}, 587 CanBeArgRet: canBeArg, 588 CantBeOut: true, 589 CanBeTypedef: true, 590 NeedBase: true, 591 Args: []namedArg{ 592 {Name: "range start", Type: typeArgInt}, 593 {Name: "per-proc values", Type: typeArgInt}, 594 }, 595 CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 596 start := args[0].Value 597 perProc := args[1].Value 598 if perProc == 0 { 599 comp.error(args[1].Pos, "proc per-process values must not be 0") 600 return 601 } 602 size := base.TypeSize * 8 603 max := uint64(1) << size 604 if size == 64 { 605 max = ^uint64(0) 606 } 607 if start >= max { 608 comp.error(args[0].Pos, "values starting from %v overflow base type", start) 609 } else if perProc > (max-start)/prog.MaxPids { 610 comp.error(args[0].Pos, "values starting from %v with step %v overflow base type for %v procs", 611 start, perProc, prog.MaxPids) 612 } 613 }, 614 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 615 base.TypeAlign = getIntAlignment(comp, base) 616 return &prog.ProcType{ 617 IntTypeCommon: base, 618 ValuesStart: args[0].Value, 619 ValuesPerProc: args[1].Value, 620 } 621 }, 622 } 623 624 var typeText = &typeDesc{ 625 Names: []string{"text"}, 626 CantBeOpt: true, 627 CantBeOut: true, 628 Args: []namedArg{{Name: "kind", Type: typeArgTextType}}, 629 Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 630 return true 631 }, 632 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 633 base.TypeSize = 0 634 base.TypeAlign = 1 635 return &prog.BufferType{ 636 TypeCommon: base.TypeCommon, 637 Kind: prog.BufferText, 638 Text: genTextType(args[0]), 639 } 640 }, 641 } 642 643 var typeArgTextType = &typeArg{ 644 Kind: kindIdent, 645 Names: []string{"target", "x86_real", "x86_16", "x86_32", "x86_64", "arm64", "ppc64"}, 646 } 647 648 func genTextType(t *ast.Type) prog.TextKind { 649 switch t.Ident { 650 case "target": 651 return prog.TextTarget 652 case "x86_real": 653 return prog.TextX86Real 654 case "x86_16": 655 return prog.TextX86bit16 656 case "x86_32": 657 return prog.TextX86bit32 658 case "x86_64": 659 return prog.TextX86bit64 660 case "arm64": 661 return prog.TextArm64 662 case "ppc64": 663 return prog.TextPpc64 664 default: 665 panic(fmt.Sprintf("unknown text type %q", t.Ident)) 666 } 667 } 668 669 const ( 670 stringnoz = "stringnoz" 671 glob = "glob" 672 ) 673 674 var typeString = &typeDesc{ 675 Names: []string{"string", glob, stringnoz}, 676 CanBeTypedef: true, 677 OptArgs: 2, 678 Args: []namedArg{ 679 {Name: "literal or flags", Type: typeArgStringFlags}, 680 {Name: "size", Type: typeArgInt}, 681 }, 682 Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 683 if t.Ident == stringnoz && len(args) > 1 { 684 comp.error(args[0].Pos, "fixed-size string can't be non-zero-terminated") 685 } 686 if t.Ident == glob && len(args) != 1 { 687 comp.error(t.Pos, "glob only accepts 1 arg, provided %v", len(args)) 688 } 689 }, 690 CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 691 if len(args) > 1 { 692 size := args[1].Value 693 vals := comp.genStrings(t, args) 694 for _, s := range vals { 695 if uint64(len(s)) > size { 696 comp.error(args[0].Pos, "string value %q exceeds buffer length %v", 697 s, size) 698 } 699 } 700 } 701 }, 702 Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 703 if t.Ident == glob { 704 return true 705 } 706 return comp.stringSize(t, args) == varlenString 707 }, 708 ZeroSize: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 709 return comp.stringSize(t, args) == 0 710 }, 711 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 712 base.TypeAlign = 1 713 if len(args) > 0 && args[0].Ident == "filename" { 714 base.TypeName = "filename" 715 base.TypeSize = 0 716 if len(args) >= 2 { 717 base.TypeSize = args[1].Value 718 } 719 return &prog.BufferType{ 720 TypeCommon: base.TypeCommon, 721 Kind: prog.BufferFilename, 722 NoZ: t.Ident == stringnoz, 723 } 724 } 725 if len(args) > 0 && t.Ident == glob { 726 base.TypeSize = 0 727 return &prog.BufferType{ 728 TypeCommon: base.TypeCommon, 729 Kind: prog.BufferGlob, 730 SubKind: args[0].String, 731 NoZ: false, 732 } 733 } 734 subkind := "" 735 if len(args) > 0 && args[0].Ident != "" { 736 subkind = args[0].Ident 737 } 738 vals := comp.genStrings(t, args) 739 base.TypeSize = comp.stringSize(t, args) 740 if base.TypeSize == varlenString { 741 base.TypeSize = 0 742 } 743 return &prog.BufferType{ 744 TypeCommon: base.TypeCommon, 745 Kind: prog.BufferString, 746 SubKind: subkind, 747 Values: vals, 748 NoZ: t.Ident == stringnoz, 749 } 750 }, 751 } 752 753 func (comp *compiler) genStrings(t *ast.Type, args []*ast.Type) []string { 754 var vals []string 755 if len(args) > 0 { 756 if args[0].HasString { 757 vals = append(vals, args[0].String) 758 } else { 759 vals = genStrArray(comp.strFlags[args[0].Ident].Values) 760 } 761 } 762 if t.Ident == stringnoz { 763 return vals 764 } 765 var size uint64 766 if len(args) > 1 { 767 size = args[1].Value 768 } 769 for i, s := range vals { 770 s += "\x00" 771 for uint64(len(s)) < size { 772 s += "\x00" 773 } 774 vals[i] = s 775 } 776 return vals 777 } 778 779 const varlenString = ^uint64(0) 780 781 // stringSize returns static string size, or varlenString if it is variable length. 782 func (comp *compiler) stringSize(t *ast.Type, args []*ast.Type) uint64 { 783 switch len(args) { 784 case 0: 785 return varlenString // a random string 786 case 1: 787 var z uint64 788 if t.Ident == "string" { 789 z = 1 790 } 791 if args[0].HasString { 792 return uint64(len(args[0].String)) + z // string constant 793 } 794 size := varlenString 795 for _, s := range comp.strFlags[args[0].Ident].Values { 796 s1 := uint64(len(s.Value)) + z 797 if size != varlenString && size != s1 { 798 return varlenString // strings of different lengths 799 } 800 size = s1 801 } 802 return size // all strings have the same length 803 case 2: 804 return args[1].Value // have explicit length 805 default: 806 panic("too many string args") 807 } 808 } 809 810 var typeArgStringFlags = &typeArg{ 811 Kind: kindIdent | kindString, 812 Check: func(comp *compiler, t *ast.Type) { 813 if t.Ident != "" && comp.strFlags[t.Ident] == nil { 814 comp.error(t.Pos, "unknown string flags %v", t.Ident) 815 return 816 } 817 }, 818 } 819 820 var typeFmt = &typeDesc{ 821 Names: []string{"fmt"}, 822 CanBeTypedef: true, 823 CantBeOpt: true, 824 CantBeOut: true, 825 CantHaveOut: true, 826 Args: []namedArg{ 827 {Name: "format", Type: typeFmtFormat}, 828 {Name: "value", Type: typeArgType, IsArg: true}, 829 }, 830 Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 831 desc, _, _ := comp.getArgsBase(args[1], true) 832 switch desc { 833 case typeResource, typeInt, typeLen, typeFlags, typeProc: 834 default: 835 comp.error(t.Pos, "bad fmt value %v, expect an integer", args[1].Ident) 836 return 837 } 838 }, 839 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 840 var format prog.BinaryFormat 841 var size uint64 842 switch args[0].Ident { 843 case "dec": 844 format = prog.FormatStrDec 845 size = 20 846 case "hex": 847 format = prog.FormatStrHex 848 size = 18 849 case "oct": 850 format = prog.FormatStrOct 851 size = 23 852 } 853 typ := comp.genType(args[1], comp.ptrSize) 854 switch t := typ.(type) { 855 case *prog.ResourceType: 856 t.ArgFormat = format 857 t.TypeSize = size 858 t.TypeAlign = 1 859 case *prog.IntType: 860 t.ArgFormat = format 861 t.TypeSize = size 862 t.TypeAlign = 1 863 case *prog.LenType: 864 t.ArgFormat = format 865 t.TypeSize = size 866 t.TypeAlign = 1 867 case *prog.FlagsType: 868 t.ArgFormat = format 869 t.TypeSize = size 870 t.TypeAlign = 1 871 case *prog.ProcType: 872 t.ArgFormat = format 873 t.TypeSize = size 874 t.TypeAlign = 1 875 case *prog.ConstType: 876 // We don't allow fmt[const] directly, but flags with only 1 value 877 // are transformed to ConstType. 878 t.ArgFormat = format 879 t.TypeSize = size 880 t.TypeAlign = 1 881 default: 882 panic(fmt.Sprintf("unexpected type: %#v", typ)) 883 } 884 return typ 885 }, 886 } 887 888 var typeFmtFormat = &typeArg{ 889 Names: []string{"dec", "hex", "oct"}, 890 Kind: kindIdent, 891 } 892 893 // typeCompressedImage is used for compressed disk images. 894 var typeCompressedImage = &typeDesc{ 895 Names: []string{"compressed_image"}, 896 CantBeOpt: true, 897 CantBeOut: true, 898 RequiresCallAttrs: map[string]bool{ 899 "no_generate": true, 900 "no_minimize": true, 901 }, 902 CanBeArgRet: func(comp *compiler, t *ast.Type) (bool, bool) { 903 return true, false 904 }, 905 Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 906 return true 907 }, 908 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 909 base.TypeSize = 0 910 base.TypeAlign = 1 911 return &prog.BufferType{ 912 TypeCommon: base.TypeCommon, 913 Kind: prog.BufferCompressed, 914 } 915 }, 916 } 917 918 // typeArgType is used as placeholder for any type (e.g. ptr target type). 919 var typeArgType = &typeArg{} 920 921 var typeResource = &typeDesc{ 922 // No Names, but getTypeDesc knows how to match it. 923 CanBeArgRet: canBeArgRet, 924 CanBeResourceBase: func(comp *compiler, t *ast.Type) bool { 925 return true 926 }, 927 // Gen is assigned below to avoid initialization loop. 928 } 929 930 func init() { 931 typeResource.Gen = func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 932 // Find and generate base type to get its size. 933 var baseType *ast.Type 934 for r := comp.resources[t.Ident]; r != nil; { 935 baseType = r.Base 936 r = comp.resources[r.Base.Ident] 937 } 938 baseProgType := comp.genType(baseType, 0) 939 base.TypeSize = baseProgType.Size() 940 base.TypeAlign = getIntAlignment(comp, base) 941 return &prog.ResourceType{ 942 TypeCommon: base.TypeCommon, 943 ArgFormat: baseProgType.Format(), 944 } 945 } 946 } 947 948 var typeStruct = &typeDesc{ 949 // No Names, but getTypeDesc knows how to match it. 950 CantBeOpt: true, 951 CanBeTypedef: true, 952 // Varlen/Gen are assigned below due to initialization cycle. 953 } 954 955 func init() { 956 typeStruct.CanBeArgRet = func(comp *compiler, t *ast.Type) (bool, bool) { 957 // Allow unions to be arg if all options can be arg. 958 s := comp.structs[t.Ident] 959 if !s.IsUnion { 960 return false, false 961 } 962 canBeArg := true 963 for _, fld := range s.Fields { 964 desc := comp.getTypeDesc(fld.Type) 965 if desc == nil || desc == typeStruct || desc.CanBeArgRet == nil { 966 return false, false 967 } 968 canBeArg1, _ := desc.CanBeArgRet(comp, fld.Type) 969 if !canBeArg1 { 970 canBeArg = false 971 } 972 } 973 return canBeArg, false 974 } 975 typeStruct.Varlen = func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 976 return comp.structIsVarlen(t.Ident) 977 } 978 typeStruct.ZeroSize = func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 979 for _, fld := range comp.structs[t.Ident].Fields { 980 if !comp.isZeroSize(fld.Type) { 981 return false 982 } 983 } 984 return true 985 } 986 typeStruct.Gen = func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 987 if typ := comp.structTypes[t.Ident]; typ != nil { 988 return typ 989 } 990 s := comp.structs[t.Ident] 991 common := genCommon(t.Ident, sizeUnassigned, false) 992 common.IsVarlen = typeStruct.Varlen(comp, t, args) 993 var typ prog.Type 994 if s.IsUnion { 995 typ = &prog.UnionType{ 996 TypeCommon: common, 997 } 998 } else { 999 typ = &prog.StructType{ 1000 TypeCommon: common, 1001 } 1002 } 1003 // Need to cache type in structTypes before generating fields to break recursion. 1004 comp.structTypes[t.Ident] = typ 1005 fields, overlayField := comp.genFieldArray(s.Fields, make([]uint64, len(s.Fields))) 1006 switch typ1 := typ.(type) { 1007 case *prog.UnionType: 1008 typ1.Fields = fields 1009 for _, f := range fields { 1010 if a := f.Type.Alignment(); typ1.TypeAlign < a { 1011 typ1.TypeAlign = a 1012 } 1013 } 1014 case *prog.StructType: 1015 typ1.Fields = fields 1016 for i, field := range fields { 1017 if field.Condition != nil { 1018 fields[i] = comp.wrapConditionalField(t.Ident, field) 1019 } 1020 } 1021 if overlayField >= 0 { 1022 typ1.OverlayField = overlayField 1023 } 1024 attrs := comp.parseIntAttrs(structAttrs, s, s.Attrs) 1025 if align := attrs[attrAlign]; align != 0 { 1026 typ1.TypeAlign = align 1027 } else if attrs[attrPacked] != 0 { 1028 typ1.TypeAlign = 1 1029 } else { 1030 for _, f := range fields { 1031 a := f.Type.Alignment() 1032 if typ1.TypeAlign < a { 1033 typ1.TypeAlign = a 1034 } 1035 } 1036 } 1037 } 1038 return typ 1039 } 1040 } 1041 1042 var typeTypedef = &typeDesc{ 1043 // No Names, but getTypeDesc knows how to match it. 1044 Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 1045 panic("must not be called") 1046 }, 1047 } 1048 1049 var typeArgDir = &typeArg{ 1050 Kind: kindIdent, 1051 Names: []string{"in", "out", "inout"}, 1052 } 1053 1054 func genDir(t *ast.Type) prog.Dir { 1055 switch t.Ident { 1056 case "in": 1057 return prog.DirIn 1058 case "out": 1059 return prog.DirOut 1060 case "inout": 1061 return prog.DirInOut 1062 default: 1063 panic(fmt.Sprintf("unknown direction %q", t.Ident)) 1064 } 1065 } 1066 1067 var typeArgInt = &typeArg{ 1068 Kind: kindInt, 1069 } 1070 1071 var typeArgIntValue = &typeArg{ 1072 Kind: kindInt | kindIdent, 1073 MaxColon: 1, 1074 CheckConsts: func(comp *compiler, t *ast.Type) { 1075 // If the first arg is not a range, then it should be a valid flags. 1076 if len(t.Colon) == 0 && t.Ident != "" && comp.intFlags[t.Ident] == nil { 1077 comp.error(t.Pos, "unknown flags %v", t.Ident) 1078 return 1079 } 1080 }, 1081 } 1082 1083 var typeArgIntAlign = &typeArg{ 1084 Kind: kindInt, 1085 MaxColon: 0, 1086 CheckConsts: func(comp *compiler, t *ast.Type) { 1087 if t.Value <= 1 { 1088 comp.error(t.Pos, "bad int alignment %v", t.Value) 1089 } 1090 }, 1091 } 1092 1093 // Size of array and vma's. 1094 var typeArgSizeRange = &typeArg{ 1095 Kind: kindInt, 1096 MaxColon: 1, 1097 CheckConsts: func(comp *compiler, t *ast.Type) { 1098 end := t.Value 1099 if len(t.Colon) != 0 { 1100 end = t.Colon[0].Value 1101 } 1102 const maxVal = 1e6 1103 if t.Value > end || t.Value > maxVal || end > maxVal { 1104 comp.error(t.Pos, "bad size range [%v:%v]", t.Value, end) 1105 } 1106 }, 1107 } 1108 1109 // Base type of const/len/etc. Same as typeInt, but can't have range. 1110 var typeArgBase = namedArg{ 1111 Name: "base type", 1112 Type: &typeArg{ 1113 Names: []string{"int8", "int16", "int32", "int64", "int16be", "int32be", "int64be", "intptr"}, 1114 MaxColon: 1, 1115 Check: func(comp *compiler, t *ast.Type) { 1116 if len(t.Colon) != 0 { 1117 col := t.Colon[0] 1118 if col.Ident != "" { 1119 comp.error(col.Pos, "literal const bitfield sizes are not supported") 1120 return 1121 } 1122 if col.Value == 0 { 1123 // This was not supported historically 1124 // and does not work the way C bitfields of size 0 work. 1125 // We could allow this, but then we need to make 1126 // this work the way C bitfields work. 1127 comp.error(col.Pos, "bitfields of size 0 are not supported") 1128 } 1129 size, _ := comp.parseIntType(t.Ident) 1130 if col.Value > size*8 { 1131 comp.error(col.Pos, "bitfield of size %v is too large for base type of size %v", 1132 col.Value, size*8) 1133 } 1134 } 1135 }, 1136 }, 1137 } 1138 1139 var ( 1140 builtinTypes = make(map[string]*typeDesc) 1141 builtinDescs *ast.Description 1142 1143 // To avoid weird cases like ptr[in, in] and ptr[out, opt]. 1144 reservedName = map[string]bool{ 1145 "opt": true, 1146 "in": true, 1147 "out": true, 1148 "inout": true, 1149 } 1150 ) 1151 1152 const builtinDefs = ` 1153 type bool8 int8[0:1] 1154 type bool16 int16[0:1] 1155 type bool32 int32[0:1] 1156 type bool64 int64[0:1] 1157 type boolptr intptr[0:1] 1158 1159 type fileoff[BASE] BASE 1160 1161 type filename string[filename] 1162 filename = "", "." 1163 1164 type buffer[DIR] ptr[DIR, array[int8]] 1165 1166 type optional[T] [ 1167 val T 1168 void void 1169 ] [varlen] 1170 1171 # prog/any.go knows layout of these types. 1172 ANYUNION [ 1173 ANYBLOB array[int8] 1174 ANYRES8 ANYRES8 1175 ANYRES16 ANYRES16 1176 ANYRES32 ANYRES32 1177 ANYRES64 ANYRES64 1178 ANYRESDEC fmt[dec, ANYRES64] (in) 1179 ANYRESHEX fmt[hex, ANYRES64] (in) 1180 ANYRESOCT fmt[oct, ANYRES64] (in) 1181 ] [varlen] 1182 1183 ANYPTRS [ 1184 ANYPTR ptr[inout, array[ANYUNION]] 1185 ANYPTR64 ptr64[inout, array[ANYUNION]] 1186 ] 1187 1188 resource ANYRES8[int8]: -1, 0 1189 resource ANYRES16[int16]: -1, 0 1190 resource ANYRES32[int32]: -1, 0 1191 resource ANYRES64[int64]: -1, 0 1192 1193 syz_builtin0(a ptr[in, ANYPTRS]) (disabled) 1194 syz_builtin1(a ptr[inout, ANYUNION]) (disabled) 1195 syz_builtin2() ANYRES8 (disabled) 1196 syz_builtin3() ANYRES16 (disabled) 1197 syz_builtin4() ANYRES32 (disabled) 1198 syz_builtin5() ANYRES64 (disabled) 1199 ` 1200 1201 func init() { 1202 builtins := []*typeDesc{ 1203 typeInt, 1204 typePtr, 1205 typeVoid, 1206 typeArray, 1207 typeLen, 1208 typeConst, 1209 typeFlags, 1210 typeVMA, 1211 typeCsum, 1212 typeProc, 1213 typeText, 1214 typeString, 1215 typeFmt, 1216 typeCompressedImage, 1217 } 1218 for _, desc := range builtins { 1219 for _, name := range desc.Names { 1220 if builtinTypes[name] != nil { 1221 panic(fmt.Sprintf("duplicate builtin type %q", name)) 1222 } 1223 builtinTypes[name] = desc 1224 } 1225 } 1226 builtinDescs = ast.Parse([]byte(builtinDefs), ast.BuiltinFile, func(pos ast.Pos, msg string) { 1227 panic(fmt.Sprintf("failed to parse builtins: %v: %v", pos, msg)) 1228 }) 1229 }