github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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/TypeAlign are assigned later in layoutArray. 362 return &prog.ArrayType{ 363 TypeCommon: base.TypeCommon, 364 Elem: elemType, 365 Kind: kind, 366 RangeBegin: begin, 367 RangeEnd: end, 368 } 369 }, 370 } 371 372 var typeLen = &typeDesc{ 373 Names: []string{"len", "bytesize", "bytesize2", "bytesize4", "bytesize8", "bitsize", "offsetof"}, 374 CanBeArgRet: canBeArg, 375 CantBeOpt: true, 376 CantBeOut: true, 377 NeedBase: true, 378 Args: []namedArg{{Name: "len target", Type: typeArgLenTarget}}, 379 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 380 var bitSize uint64 381 var offset bool 382 switch t.Ident { 383 case "bytesize": 384 bitSize = 8 385 case "bytesize2", "bytesize4", "bytesize8": 386 byteSize, _ := strconv.ParseUint(t.Ident[8:], 10, 8) 387 bitSize = byteSize * 8 388 case "bitsize": 389 bitSize = 1 390 case "offsetof": 391 bitSize = 8 392 offset = true 393 } 394 path := []string{args[0].Ident} 395 for _, col := range args[0].Colon { 396 path = append(path, col.Ident) 397 } 398 base.TypeAlign = getIntAlignment(comp, base) 399 return &prog.LenType{ 400 IntTypeCommon: base, 401 Path: path, 402 BitSize: bitSize, 403 Offset: offset, 404 } 405 }, 406 } 407 408 var typeConst = &typeDesc{ 409 Names: []string{"const"}, 410 CanBeArgRet: canBeArg, 411 CanBeTypedef: true, 412 CantBeOpt: true, 413 CantBeOut: true, 414 NeedBase: true, 415 Args: []namedArg{{Name: "value", Type: typeArgInt}}, 416 CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 417 v := args[0].Value 418 bitSize := base.TypeBitSize() 419 if constOverflowsBase(v, base) { 420 comp.error(args[0].Pos, "const val 0x%x does not fit into %v bits", v, bitSize) 421 } 422 args[0].Value = v & (uint64(1)<<bitSize - 1) 423 }, 424 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 425 base.TypeAlign = getIntAlignment(comp, base) 426 return &prog.ConstType{ 427 IntTypeCommon: base, 428 Val: args[0].Value, 429 } 430 }, 431 } 432 433 func constOverflowsBase(v uint64, base prog.IntTypeCommon) bool { 434 size := base.TypeBitSize() 435 if size == 64 { 436 return false 437 } 438 mask := uint64(1)<<size - 1 439 v1 := v & mask 440 if int64(v1<<(64-size)) < 0 && int64(v) < 0 { 441 v1 |= ^mask 442 } 443 return v1 != v 444 } 445 446 var typeArgLenTarget = &typeArg{ 447 Kind: kindIdent, 448 MaxColon: 10, 449 } 450 451 var typeFlags = &typeDesc{ 452 Names: []string{"flags"}, 453 CanBeArgRet: canBeArg, 454 CanBeTypedef: true, 455 CantBeOpt: true, 456 CantBeOut: true, 457 NeedBase: true, 458 Args: []namedArg{{Name: "flags", Type: typeArgFlags}}, 459 CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 460 name := args[0].Ident 461 if name == "xdp_mmap_offsets" && comp.ptrSize == 4 { 462 // TODO(dvyukov): this sucks a lot. It seems that out 32-bit mmap is wrong. 463 // The syscall accepts number of pages as int32, but we pass offset in bytes. 464 // As the result large XDP consts don't fit into the arg. 465 return 466 } 467 f := comp.intFlags[name] 468 for _, val := range f.Values { 469 if constOverflowsBase(val.Value, base) { 470 comp.error(args[0].Pos, "%v %v=0x%x doesn't fit into %v bits", 471 name, val.Ident, val.Value, base.TypeBitSize()) 472 } 473 } 474 }, 475 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 476 base.TypeAlign = getIntAlignment(comp, base) 477 return generateFlagsType(comp, base, args[0].Ident) 478 }, 479 } 480 481 func isBitmask(values []uint64) bool { 482 if values[0] == 0 { 483 // 0 can't be part of bitmask, this helps to handle important 484 // case like "0, 1" and "0, 1, 2" that would be detected 485 // as bitmask otherwise. 486 return false 487 } 488 var combined uint64 489 for _, v := range values { 490 if v&combined != 0 { 491 return false 492 } 493 combined |= v 494 } 495 return true 496 } 497 498 var typeArgFlags = &typeArg{ 499 Kind: kindIdent, 500 Check: func(comp *compiler, t *ast.Type) { 501 if comp.intFlags[t.Ident] == nil { 502 comp.error(t.Pos, "unknown flags %v", t.Ident) 503 return 504 } 505 }, 506 } 507 508 var typeVMA = &typeDesc{ 509 Names: []string{"vma", "vma64"}, 510 CanBeArgRet: canBeArg, 511 OptArgs: 1, 512 Args: []namedArg{{Name: "size range", Type: typeArgSizeRange}}, 513 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 514 var begin, end uint64 515 if len(args) > 0 { 516 begin, end = args[0].Value, args[0].Value 517 if len(args[0].Colon) != 0 { 518 end = args[0].Colon[0].Value 519 } 520 } 521 base.TypeSize = comp.ptrSize 522 if t.Ident == "vma64" { 523 base.TypeSize = 8 524 } 525 base.TypeAlign = getIntAlignment(comp, base) 526 return &prog.VmaType{ 527 TypeCommon: base.TypeCommon, 528 RangeBegin: begin, 529 RangeEnd: end, 530 } 531 }, 532 } 533 534 var typeCsum = &typeDesc{ 535 Names: []string{"csum"}, 536 NeedBase: true, 537 CantBeOpt: true, 538 CantBeOut: true, 539 OptArgs: 1, 540 Args: []namedArg{ 541 {Name: "csum target", Type: typeArgLenTarget}, 542 {Name: "kind", Type: typeArgCsumType}, 543 {Name: "proto", Type: typeArgInt}, 544 }, 545 Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 546 if len(args) > 2 && genCsumKind(args[1]) != prog.CsumPseudo { 547 comp.error(args[2].Pos, "only pseudo csum can have proto") 548 } 549 if len(args[0].Colon) != 0 { 550 comp.error(args[0].Colon[0].Pos, "path expressions are not implemented for csum") 551 } 552 }, 553 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 554 var proto uint64 555 if len(args) > 2 { 556 proto = args[2].Value 557 } 558 base.TypeAlign = getIntAlignment(comp, base) 559 return &prog.CsumType{ 560 IntTypeCommon: base, 561 Buf: args[0].Ident, 562 Kind: genCsumKind(args[1]), 563 Protocol: proto, 564 } 565 }, 566 } 567 568 var typeArgCsumType = &typeArg{ 569 Kind: kindIdent, 570 Names: []string{"inet", "pseudo"}, 571 } 572 573 func genCsumKind(t *ast.Type) prog.CsumKind { 574 switch t.Ident { 575 case "inet": 576 return prog.CsumInet 577 case "pseudo": 578 return prog.CsumPseudo 579 default: 580 panic(fmt.Sprintf("unknown csum kind %q", t.Ident)) 581 } 582 } 583 584 var typeProc = &typeDesc{ 585 Names: []string{"proc"}, 586 CanBeArgRet: canBeArg, 587 CantBeOut: true, 588 CanBeTypedef: true, 589 NeedBase: true, 590 Args: []namedArg{ 591 {Name: "range start", Type: typeArgInt}, 592 {Name: "per-proc values", Type: typeArgInt}, 593 }, 594 CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 595 start := args[0].Value 596 perProc := args[1].Value 597 if perProc == 0 { 598 comp.error(args[1].Pos, "proc per-process values must not be 0") 599 return 600 } 601 size := base.TypeSize * 8 602 max := uint64(1) << size 603 if size == 64 { 604 max = ^uint64(0) 605 } 606 if start >= max { 607 comp.error(args[0].Pos, "values starting from %v overflow base type", start) 608 } else if perProc > (max-start)/prog.MaxPids { 609 comp.error(args[0].Pos, "values starting from %v with step %v overflow base type for %v procs", 610 start, perProc, prog.MaxPids) 611 } 612 }, 613 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 614 base.TypeAlign = getIntAlignment(comp, base) 615 return &prog.ProcType{ 616 IntTypeCommon: base, 617 ValuesStart: args[0].Value, 618 ValuesPerProc: args[1].Value, 619 } 620 }, 621 } 622 623 var typeText = &typeDesc{ 624 Names: []string{"text"}, 625 CantBeOpt: true, 626 CantBeOut: true, 627 Args: []namedArg{{Name: "kind", Type: typeArgTextType}}, 628 Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 629 return true 630 }, 631 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 632 base.TypeSize = 0 633 base.TypeAlign = 1 634 return &prog.BufferType{ 635 TypeCommon: base.TypeCommon, 636 Kind: prog.BufferText, 637 Text: genTextType(args[0]), 638 } 639 }, 640 } 641 642 var typeArgTextType = &typeArg{ 643 Kind: kindIdent, 644 Names: []string{"target", "x86_real", "x86_16", "x86_32", "x86_64", "arm64", "ppc64"}, 645 } 646 647 func genTextType(t *ast.Type) prog.TextKind { 648 switch t.Ident { 649 case "target": 650 return prog.TextTarget 651 case "x86_real": 652 return prog.TextX86Real 653 case "x86_16": 654 return prog.TextX86bit16 655 case "x86_32": 656 return prog.TextX86bit32 657 case "x86_64": 658 return prog.TextX86bit64 659 case "arm64": 660 return prog.TextArm64 661 case "ppc64": 662 return prog.TextPpc64 663 default: 664 panic(fmt.Sprintf("unknown text type %q", t.Ident)) 665 } 666 } 667 668 const ( 669 stringnoz = "stringnoz" 670 glob = "glob" 671 ) 672 673 var typeString = &typeDesc{ 674 Names: []string{"string", glob, stringnoz}, 675 CanBeTypedef: true, 676 OptArgs: 2, 677 Args: []namedArg{ 678 {Name: "literal or flags", Type: typeArgStringFlags}, 679 {Name: "size", Type: typeArgInt}, 680 }, 681 Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 682 if t.Ident == stringnoz && len(args) > 1 { 683 comp.error(args[0].Pos, "fixed-size string can't be non-zero-terminated") 684 } 685 if t.Ident == glob && len(args) != 1 { 686 comp.error(t.Pos, "glob only accepts 1 arg, provided %v", len(args)) 687 } 688 }, 689 CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 690 if len(args) > 1 { 691 size := args[1].Value 692 vals := comp.genStrings(t, args) 693 for _, s := range vals { 694 if uint64(len(s)) > size { 695 comp.error(args[0].Pos, "string value %q exceeds buffer length %v", 696 s, size) 697 } 698 } 699 } 700 }, 701 Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 702 if t.Ident == glob { 703 return true 704 } 705 return comp.stringSize(t, args) == varlenString 706 }, 707 ZeroSize: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 708 return comp.stringSize(t, args) == 0 709 }, 710 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 711 base.TypeAlign = 1 712 if len(args) > 0 && args[0].Ident == "filename" { 713 base.TypeName = "filename" 714 base.TypeSize = 0 715 if len(args) >= 2 { 716 base.TypeSize = args[1].Value 717 } 718 return &prog.BufferType{ 719 TypeCommon: base.TypeCommon, 720 Kind: prog.BufferFilename, 721 NoZ: t.Ident == stringnoz, 722 } 723 } 724 if len(args) > 0 && t.Ident == glob { 725 base.TypeSize = 0 726 return &prog.BufferType{ 727 TypeCommon: base.TypeCommon, 728 Kind: prog.BufferGlob, 729 SubKind: args[0].String, 730 NoZ: false, 731 } 732 } 733 subkind := "" 734 if len(args) > 0 && args[0].Ident != "" { 735 subkind = args[0].Ident 736 } 737 vals := comp.genStrings(t, args) 738 base.TypeSize = comp.stringSize(t, args) 739 if base.TypeSize == varlenString { 740 base.TypeSize = 0 741 } 742 return &prog.BufferType{ 743 TypeCommon: base.TypeCommon, 744 Kind: prog.BufferString, 745 SubKind: subkind, 746 Values: vals, 747 NoZ: t.Ident == stringnoz, 748 } 749 }, 750 } 751 752 func (comp *compiler) genStrings(t *ast.Type, args []*ast.Type) []string { 753 var vals []string 754 if len(args) > 0 { 755 if args[0].HasString { 756 vals = append(vals, args[0].String) 757 } else { 758 vals = genStrArray(comp.strFlags[args[0].Ident].Values) 759 } 760 } 761 if t.Ident == stringnoz { 762 return vals 763 } 764 var size uint64 765 if len(args) > 1 { 766 size = args[1].Value 767 } 768 for i, s := range vals { 769 s += "\x00" 770 for uint64(len(s)) < size { 771 s += "\x00" 772 } 773 vals[i] = s 774 } 775 return vals 776 } 777 778 const varlenString = ^uint64(0) 779 780 // stringSize returns static string size, or varlenString if it is variable length. 781 func (comp *compiler) stringSize(t *ast.Type, args []*ast.Type) uint64 { 782 switch len(args) { 783 case 0: 784 return varlenString // a random string 785 case 1: 786 var z uint64 787 if t.Ident == "string" { 788 z = 1 789 } 790 if args[0].HasString { 791 return uint64(len(args[0].String)) + z // string constant 792 } 793 size := varlenString 794 for _, s := range comp.strFlags[args[0].Ident].Values { 795 s1 := uint64(len(s.Value)) + z 796 if size != varlenString && size != s1 { 797 return varlenString // strings of different lengths 798 } 799 size = s1 800 } 801 return size // all strings have the same length 802 case 2: 803 return args[1].Value // have explicit length 804 default: 805 panic("too many string args") 806 } 807 } 808 809 var typeArgStringFlags = &typeArg{ 810 Kind: kindIdent | kindString, 811 Check: func(comp *compiler, t *ast.Type) { 812 if t.Ident != "" && comp.strFlags[t.Ident] == nil { 813 comp.error(t.Pos, "unknown string flags %v", t.Ident) 814 return 815 } 816 }, 817 } 818 819 var typeFmt = &typeDesc{ 820 Names: []string{"fmt"}, 821 CanBeTypedef: true, 822 CantBeOpt: true, 823 CantBeOut: true, 824 CantHaveOut: true, 825 Args: []namedArg{ 826 {Name: "format", Type: typeFmtFormat}, 827 {Name: "value", Type: typeArgType, IsArg: true}, 828 }, 829 Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 830 desc, _, _ := comp.getArgsBase(args[1], true) 831 switch desc { 832 case typeResource, typeInt, typeLen, typeFlags, typeProc: 833 default: 834 comp.error(t.Pos, "bad fmt value %v, expect an integer", args[1].Ident) 835 return 836 } 837 }, 838 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 839 var format prog.BinaryFormat 840 var size uint64 841 switch args[0].Ident { 842 case "dec": 843 format = prog.FormatStrDec 844 size = 20 845 case "hex": 846 format = prog.FormatStrHex 847 size = 18 848 case "oct": 849 format = prog.FormatStrOct 850 size = 23 851 } 852 typ := comp.genType(args[1], comp.ptrSize) 853 switch t := typ.(type) { 854 case *prog.ResourceType: 855 t.ArgFormat = format 856 t.TypeSize = size 857 t.TypeAlign = 1 858 case *prog.IntType: 859 t.ArgFormat = format 860 t.TypeSize = size 861 t.TypeAlign = 1 862 case *prog.LenType: 863 t.ArgFormat = format 864 t.TypeSize = size 865 t.TypeAlign = 1 866 case *prog.FlagsType: 867 t.ArgFormat = format 868 t.TypeSize = size 869 t.TypeAlign = 1 870 case *prog.ProcType: 871 t.ArgFormat = format 872 t.TypeSize = size 873 t.TypeAlign = 1 874 case *prog.ConstType: 875 // We don't allow fmt[const] directly, but flags with only 1 value 876 // are transformed to ConstType. 877 t.ArgFormat = format 878 t.TypeSize = size 879 t.TypeAlign = 1 880 default: 881 panic(fmt.Sprintf("unexpected type: %#v", typ)) 882 } 883 return typ 884 }, 885 } 886 887 var typeFmtFormat = &typeArg{ 888 Names: []string{"dec", "hex", "oct"}, 889 Kind: kindIdent, 890 } 891 892 // typeCompressedImage is used for compressed disk images. 893 var typeCompressedImage = &typeDesc{ 894 Names: []string{"compressed_image"}, 895 CantBeOpt: true, 896 CantBeOut: true, 897 RequiresCallAttrs: map[string]bool{ 898 "no_generate": true, 899 "no_minimize": true, 900 }, 901 CanBeArgRet: func(comp *compiler, t *ast.Type) (bool, bool) { 902 return true, false 903 }, 904 Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 905 return true 906 }, 907 Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 908 base.TypeSize = 0 909 base.TypeAlign = 1 910 return &prog.BufferType{ 911 TypeCommon: base.TypeCommon, 912 Kind: prog.BufferCompressed, 913 } 914 }, 915 } 916 917 // typeArgType is used as placeholder for any type (e.g. ptr target type). 918 var typeArgType = &typeArg{} 919 920 var typeResource = &typeDesc{ 921 // No Names, but getTypeDesc knows how to match it. 922 CanBeArgRet: canBeArgRet, 923 CanBeResourceBase: func(comp *compiler, t *ast.Type) bool { 924 return true 925 }, 926 // Gen is assigned below to avoid initialization loop. 927 } 928 929 func init() { 930 typeResource.Gen = func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 931 // Find and generate base type to get its size. 932 var baseType *ast.Type 933 for r := comp.resources[t.Ident]; r != nil; { 934 baseType = r.Base 935 r = comp.resources[r.Base.Ident] 936 } 937 baseProgType := comp.genType(baseType, 0) 938 base.TypeSize = baseProgType.Size() 939 base.TypeAlign = getIntAlignment(comp, base) 940 return &prog.ResourceType{ 941 TypeCommon: base.TypeCommon, 942 ArgFormat: baseProgType.Format(), 943 } 944 } 945 } 946 947 var typeStruct = &typeDesc{ 948 // No Names, but getTypeDesc knows how to match it. 949 CantBeOpt: true, 950 CanBeTypedef: true, 951 // Varlen/Gen are assigned below due to initialization cycle. 952 } 953 954 func init() { 955 typeStruct.CanBeArgRet = func(comp *compiler, t *ast.Type) (bool, bool) { 956 // Allow unions to be arg if all options can be arg. 957 s := comp.structs[t.Ident] 958 if !s.IsUnion { 959 return false, false 960 } 961 canBeArg := true 962 for _, fld := range s.Fields { 963 desc := comp.getTypeDesc(fld.Type) 964 if desc == nil || desc == typeStruct || desc.CanBeArgRet == nil { 965 return false, false 966 } 967 canBeArg1, _ := desc.CanBeArgRet(comp, fld.Type) 968 if !canBeArg1 { 969 canBeArg = false 970 } 971 } 972 return canBeArg, false 973 } 974 typeStruct.Varlen = func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 975 return comp.structIsVarlen(t.Ident) 976 } 977 typeStruct.ZeroSize = func(comp *compiler, t *ast.Type, args []*ast.Type) bool { 978 for _, fld := range comp.structs[t.Ident].Fields { 979 if !comp.isZeroSize(fld.Type) { 980 return false 981 } 982 } 983 return true 984 } 985 typeStruct.Gen = func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { 986 if typ := comp.structTypes[t.Ident]; typ != nil { 987 return typ 988 } 989 s := comp.structs[t.Ident] 990 common := genCommon(t.Ident, sizeUnassigned, false) 991 common.IsVarlen = typeStruct.Varlen(comp, t, args) 992 var typ prog.Type 993 if s.IsUnion { 994 typ = &prog.UnionType{ 995 TypeCommon: common, 996 } 997 } else { 998 typ = &prog.StructType{ 999 TypeCommon: common, 1000 } 1001 } 1002 // Need to cache type in structTypes before generating fields to break recursion. 1003 comp.structTypes[t.Ident] = typ 1004 fields, overlayField := comp.genFieldArray(s.Fields, make([]uint64, len(s.Fields))) 1005 switch typ1 := typ.(type) { 1006 case *prog.UnionType: 1007 typ1.Fields = fields 1008 case *prog.StructType: 1009 typ1.Fields = fields 1010 for i, field := range fields { 1011 if field.Condition != nil { 1012 fields[i] = comp.wrapConditionalField(t.Ident, field) 1013 } 1014 } 1015 if overlayField >= 0 { 1016 typ1.OverlayField = overlayField 1017 } 1018 } 1019 // TypeSize/TypeAlign are assigned later in layoutStruct. 1020 return typ 1021 } 1022 } 1023 1024 var typeTypedef = &typeDesc{ 1025 // No Names, but getTypeDesc knows how to match it. 1026 Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { 1027 panic("must not be called") 1028 }, 1029 } 1030 1031 var typeArgDir = &typeArg{ 1032 Kind: kindIdent, 1033 Names: []string{"in", "out", "inout"}, 1034 } 1035 1036 func genDir(t *ast.Type) prog.Dir { 1037 switch t.Ident { 1038 case "in": 1039 return prog.DirIn 1040 case "out": 1041 return prog.DirOut 1042 case "inout": 1043 return prog.DirInOut 1044 default: 1045 panic(fmt.Sprintf("unknown direction %q", t.Ident)) 1046 } 1047 } 1048 1049 var typeArgInt = &typeArg{ 1050 Kind: kindInt, 1051 } 1052 1053 var typeArgIntValue = &typeArg{ 1054 Kind: kindInt | kindIdent, 1055 MaxColon: 1, 1056 CheckConsts: func(comp *compiler, t *ast.Type) { 1057 // If the first arg is not a range, then it should be a valid flags. 1058 if len(t.Colon) == 0 && t.Ident != "" && comp.intFlags[t.Ident] == nil { 1059 comp.error(t.Pos, "unknown flags %v", t.Ident) 1060 return 1061 } 1062 }, 1063 } 1064 1065 var typeArgIntAlign = &typeArg{ 1066 Kind: kindInt, 1067 MaxColon: 0, 1068 CheckConsts: func(comp *compiler, t *ast.Type) { 1069 if t.Value <= 1 { 1070 comp.error(t.Pos, "bad int alignment %v", t.Value) 1071 } 1072 }, 1073 } 1074 1075 // Size of array and vma's. 1076 var typeArgSizeRange = &typeArg{ 1077 Kind: kindInt, 1078 MaxColon: 1, 1079 CheckConsts: func(comp *compiler, t *ast.Type) { 1080 end := t.Value 1081 if len(t.Colon) != 0 { 1082 end = t.Colon[0].Value 1083 } 1084 const maxVal = 1e6 1085 if t.Value > end || t.Value > maxVal || end > maxVal { 1086 comp.error(t.Pos, "bad size range [%v:%v]", t.Value, end) 1087 } 1088 }, 1089 } 1090 1091 // Base type of const/len/etc. Same as typeInt, but can't have range. 1092 var typeArgBase = namedArg{ 1093 Name: "base type", 1094 Type: &typeArg{ 1095 Names: []string{"int8", "int16", "int32", "int64", "int16be", "int32be", "int64be", "intptr"}, 1096 MaxColon: 1, 1097 Check: func(comp *compiler, t *ast.Type) { 1098 if len(t.Colon) != 0 { 1099 col := t.Colon[0] 1100 if col.Ident != "" { 1101 comp.error(col.Pos, "literal const bitfield sizes are not supported") 1102 return 1103 } 1104 if col.Value == 0 { 1105 // This was not supported historically 1106 // and does not work the way C bitfields of size 0 work. 1107 // We could allow this, but then we need to make 1108 // this work the way C bitfields work. 1109 comp.error(col.Pos, "bitfields of size 0 are not supported") 1110 } 1111 size, _ := comp.parseIntType(t.Ident) 1112 if col.Value > size*8 { 1113 comp.error(col.Pos, "bitfield of size %v is too large for base type of size %v", 1114 col.Value, size*8) 1115 } 1116 } 1117 }, 1118 }, 1119 } 1120 1121 var ( 1122 builtinTypes = make(map[string]*typeDesc) 1123 builtinDescs *ast.Description 1124 1125 // To avoid weird cases like ptr[in, in] and ptr[out, opt]. 1126 reservedName = map[string]bool{ 1127 "opt": true, 1128 "in": true, 1129 "out": true, 1130 "inout": true, 1131 } 1132 ) 1133 1134 const builtinDefs = ` 1135 type bool8 int8[0:1] 1136 type bool16 int16[0:1] 1137 type bool32 int32[0:1] 1138 type bool64 int64[0:1] 1139 type boolptr intptr[0:1] 1140 1141 type fileoff[BASE] BASE 1142 1143 type filename string[filename] 1144 filename = "", "." 1145 1146 type buffer[DIR] ptr[DIR, array[int8]] 1147 1148 type optional[T] [ 1149 val T 1150 void void 1151 ] [varlen] 1152 1153 # prog/any.go knows layout of these types. 1154 ANYUNION [ 1155 ANYBLOB array[int8] 1156 ANYRES8 ANYRES8 1157 ANYRES16 ANYRES16 1158 ANYRES32 ANYRES32 1159 ANYRES64 ANYRES64 1160 ANYRESDEC fmt[dec, ANYRES64] (in) 1161 ANYRESHEX fmt[hex, ANYRES64] (in) 1162 ANYRESOCT fmt[oct, ANYRES64] (in) 1163 ] [varlen] 1164 1165 ANYPTRS [ 1166 ANYPTR ptr[inout, array[ANYUNION]] 1167 ANYPTR64 ptr64[inout, array[ANYUNION]] 1168 ] 1169 1170 resource ANYRES8[int8]: -1, 0 1171 resource ANYRES16[int16]: -1, 0 1172 resource ANYRES32[int32]: -1, 0 1173 resource ANYRES64[int64]: -1, 0 1174 1175 syz_builtin0(a ptr[in, ANYPTRS]) (disabled) 1176 syz_builtin1(a ptr[inout, ANYUNION]) (disabled) 1177 syz_builtin2() ANYRES8 (disabled) 1178 syz_builtin3() ANYRES16 (disabled) 1179 syz_builtin4() ANYRES32 (disabled) 1180 syz_builtin5() ANYRES64 (disabled) 1181 ` 1182 1183 func init() { 1184 builtins := []*typeDesc{ 1185 typeInt, 1186 typePtr, 1187 typeVoid, 1188 typeArray, 1189 typeLen, 1190 typeConst, 1191 typeFlags, 1192 typeVMA, 1193 typeCsum, 1194 typeProc, 1195 typeText, 1196 typeString, 1197 typeFmt, 1198 typeCompressedImage, 1199 } 1200 for _, desc := range builtins { 1201 for _, name := range desc.Names { 1202 if builtinTypes[name] != nil { 1203 panic(fmt.Sprintf("duplicate builtin type %q", name)) 1204 } 1205 builtinTypes[name] = desc 1206 } 1207 } 1208 builtinDescs = ast.Parse([]byte(builtinDefs), ast.BuiltinFile, func(pos ast.Pos, msg string) { 1209 panic(fmt.Sprintf("failed to parse builtins: %v: %v", pos, msg)) 1210 }) 1211 }