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 generates sys descriptions of syscalls, types and resources 5// from textual descriptions. 6package compiler 7 8import ( 9 "fmt" 10 "strings" 11 12 "github.com/google/syzkaller/pkg/ast" 13 "github.com/google/syzkaller/prog" 14) 15 16func (comp *compiler) typecheck() { 17 comp.checkDirectives() 18 comp.checkNames() 19 comp.checkFields() 20 comp.checkTypedefs() 21 comp.checkTypes() 22} 23 24func (comp *compiler) check() { 25 comp.checkTypeValues() 26 comp.checkAttributeValues() 27 comp.checkUnused() 28 comp.checkRecursion() 29 comp.checkLenTargets() 30 comp.checkConstructors() 31 comp.checkVarlens() 32 comp.checkDupConsts() 33} 34 35func (comp *compiler) checkDirectives() { 36 includes := make(map[string]bool) 37 incdirs := make(map[string]bool) 38 defines := make(map[string]bool) 39 for _, decl := range comp.desc.Nodes { 40 switch n := decl.(type) { 41 case *ast.Include: 42 name := n.File.Value 43 path := n.Pos.File + "/" + name 44 if includes[path] { 45 comp.error(n.Pos, "duplicate include %q", name) 46 } 47 includes[path] = true 48 case *ast.Incdir: 49 name := n.Dir.Value 50 path := n.Pos.File + "/" + name 51 if incdirs[path] { 52 comp.error(n.Pos, "duplicate incdir %q", name) 53 } 54 incdirs[path] = true 55 case *ast.Define: 56 name := n.Name.Name 57 path := n.Pos.File + "/" + name 58 if defines[path] { 59 comp.error(n.Pos, "duplicate define %v", name) 60 } 61 defines[path] = true 62 } 63 } 64} 65 66func (comp *compiler) checkNames() { 67 calls := make(map[string]*ast.Call) 68 for _, decl := range comp.desc.Nodes { 69 switch n := decl.(type) { 70 case *ast.Resource, *ast.Struct, *ast.TypeDef: 71 pos, typ, name := decl.Info() 72 if reservedName[name] { 73 comp.error(pos, "%v uses reserved name %v", typ, name) 74 continue 75 } 76 if builtinTypes[name] != nil || builtinTypedefs[name] != nil { 77 comp.error(pos, "%v name %v conflicts with builtin type", typ, name) 78 continue 79 } 80 if prev := comp.resources[name]; prev != nil { 81 comp.error(pos, "type %v redeclared, previously declared as resource at %v", 82 name, prev.Pos) 83 continue 84 } 85 if prev := comp.typedefs[name]; prev != nil { 86 comp.error(pos, "type %v redeclared, previously declared as type alias at %v", 87 name, prev.Pos) 88 continue 89 } 90 if prev := comp.structs[name]; prev != nil { 91 _, typ, _ := prev.Info() 92 comp.error(pos, "type %v redeclared, previously declared as %v at %v", 93 name, typ, prev.Pos) 94 continue 95 } 96 switch n := decl.(type) { 97 case *ast.Resource: 98 comp.resources[name] = n 99 case *ast.TypeDef: 100 comp.typedefs[name] = n 101 case *ast.Struct: 102 comp.structs[name] = n 103 } 104 case *ast.IntFlags: 105 name := n.Name.Name 106 if name == "_" { 107 continue 108 } 109 if reservedName[name] { 110 comp.error(n.Pos, "flags uses reserved name %v", name) 111 continue 112 } 113 if prev := comp.intFlags[name]; prev != nil { 114 comp.error(n.Pos, "flags %v redeclared, previously declared at %v", 115 name, prev.Pos) 116 continue 117 } 118 comp.intFlags[name] = n 119 case *ast.StrFlags: 120 name := n.Name.Name 121 if reservedName[name] { 122 comp.error(n.Pos, "string flags uses reserved name %v", name) 123 continue 124 } 125 if builtinStrFlags[name] != nil { 126 comp.error(n.Pos, "string flags %v conflicts with builtin flags", name) 127 continue 128 } 129 if prev := comp.strFlags[name]; prev != nil { 130 comp.error(n.Pos, "string flags %v redeclared, previously declared at %v", 131 name, prev.Pos) 132 continue 133 } 134 comp.strFlags[name] = n 135 case *ast.Call: 136 name := n.Name.Name 137 if prev := calls[name]; prev != nil { 138 comp.error(n.Pos, "syscall %v redeclared, previously declared at %v", 139 name, prev.Pos) 140 } 141 calls[name] = n 142 } 143 } 144} 145 146func (comp *compiler) checkFields() { 147 const maxArgs = 9 // executor does not support more 148 for _, decl := range comp.desc.Nodes { 149 switch n := decl.(type) { 150 case *ast.Struct: 151 _, typ, name := n.Info() 152 comp.checkStructFields(n, typ, name) 153 case *ast.TypeDef: 154 if n.Struct != nil { 155 _, typ, _ := n.Struct.Info() 156 comp.checkStructFields(n.Struct, "template "+typ, n.Name.Name) 157 } 158 case *ast.Call: 159 name := n.Name.Name 160 comp.checkFieldGroup(n.Args, "argument", "syscall "+name) 161 if len(n.Args) > maxArgs { 162 comp.error(n.Pos, "syscall %v has %v arguments, allowed maximum is %v", 163 name, len(n.Args), maxArgs) 164 } 165 } 166 } 167} 168 169func (comp *compiler) checkStructFields(n *ast.Struct, typ, name string) { 170 comp.checkFieldGroup(n.Fields, "field", typ+" "+name) 171 if len(n.Fields) < 1 { 172 comp.error(n.Pos, "%v %v has no fields, need at least 1 field", typ, name) 173 } 174} 175 176func (comp *compiler) checkFieldGroup(fields []*ast.Field, what, ctx string) { 177 existing := make(map[string]bool) 178 for _, f := range fields { 179 fn := f.Name.Name 180 if fn == "parent" { 181 comp.error(f.Pos, "reserved %v name %v in %v", what, fn, ctx) 182 } 183 if existing[fn] { 184 comp.error(f.Pos, "duplicate %v %v in %v", what, fn, ctx) 185 } 186 existing[fn] = true 187 } 188} 189 190func (comp *compiler) checkTypedefs() { 191 for _, decl := range comp.desc.Nodes { 192 switch n := decl.(type) { 193 case *ast.TypeDef: 194 if len(n.Args) == 0 { 195 // Non-template types are fully typed, so we check them ahead of time. 196 err0 := comp.errors 197 comp.checkType(checkCtx{}, n.Type, checkIsTypedef) 198 if err0 != comp.errors { 199 // To not produce confusing errors on broken type usage. 200 delete(comp.typedefs, n.Name.Name) 201 } 202 } else { 203 // For templates we only do basic checks of arguments. 204 names := make(map[string]bool) 205 for _, arg := range n.Args { 206 if names[arg.Name] { 207 comp.error(arg.Pos, "duplicate type argument %v", arg.Name) 208 } 209 names[arg.Name] = true 210 for _, c := range arg.Name { 211 if c >= 'A' && c <= 'Z' || 212 c >= '0' && c <= '9' || 213 c == '_' { 214 continue 215 } 216 comp.error(arg.Pos, "type argument %v must be ALL_CAPS", 217 arg.Name) 218 break 219 } 220 } 221 } 222 } 223 } 224} 225 226func (comp *compiler) checkTypes() { 227 for _, decl := range comp.desc.Nodes { 228 switch n := decl.(type) { 229 case *ast.Resource: 230 comp.checkType(checkCtx{}, n.Base, checkIsResourceBase) 231 case *ast.Struct: 232 comp.checkStruct(checkCtx{}, n) 233 case *ast.Call: 234 for _, a := range n.Args { 235 comp.checkType(checkCtx{}, a.Type, checkIsArg) 236 } 237 if n.Ret != nil { 238 comp.checkType(checkCtx{}, n.Ret, checkIsArg|checkIsRet) 239 } 240 } 241 } 242} 243 244func (comp *compiler) checkTypeValues() { 245 for _, decl := range comp.desc.Nodes { 246 switch decl.(type) { 247 case *ast.Call, *ast.Struct, *ast.Resource, *ast.TypeDef: 248 comp.foreachType(decl, func(t *ast.Type, desc *typeDesc, 249 args []*ast.Type, base prog.IntTypeCommon) { 250 if desc.CheckConsts != nil { 251 desc.CheckConsts(comp, t, args, base) 252 } 253 for i, arg := range args { 254 if check := desc.Args[i].Type.CheckConsts; check != nil { 255 check(comp, arg) 256 } 257 } 258 }) 259 } 260 } 261} 262 263func (comp *compiler) checkAttributeValues() { 264 for _, decl := range comp.desc.Nodes { 265 switch n := decl.(type) { 266 case *ast.Struct: 267 for _, attr := range n.Attrs { 268 if attr.Ident == "size" { 269 _, typ, name := n.Info() 270 if comp.structIsVarlen(n.Name.Name) { 271 comp.error(attr.Pos, "varlen %v %v has size attribute", 272 typ, name) 273 } 274 sz := attr.Args[0].Value 275 if sz == 0 || sz > 1<<20 { 276 comp.error(attr.Args[0].Pos, "size attribute has bad value %v"+ 277 ", expect [1, 1<<20]", sz) 278 } 279 } 280 } 281 } 282 } 283} 284 285func (comp *compiler) checkLenTargets() { 286 for _, decl := range comp.desc.Nodes { 287 switch n := decl.(type) { 288 case *ast.Call: 289 for _, arg := range n.Args { 290 comp.checkLenType(arg.Type, arg.Name.Name, n.Args, nil, make(map[string]bool), true) 291 } 292 } 293 } 294} 295 296func (comp *compiler) checkLenType(t *ast.Type, name string, fields []*ast.Field, 297 parents []*ast.Struct, checked map[string]bool, isArg bool) { 298 desc := comp.getTypeDesc(t) 299 if desc == typeStruct { 300 s := comp.structs[t.Ident] 301 // Prune recursion, can happen even on correct tree via opt pointers. 302 if checked[s.Name.Name] { 303 return 304 } 305 checked[s.Name.Name] = true 306 parents = append(parents, s) 307 if !s.IsUnion { 308 fields = s.Fields 309 } 310 for _, fld := range s.Fields { 311 comp.checkLenType(fld.Type, fld.Name.Name, fields, parents, checked, false) 312 } 313 return 314 } 315 _, args, _ := comp.getArgsBase(t, "", prog.DirIn, isArg) 316 for i, arg := range args { 317 argDesc := desc.Args[i] 318 if argDesc.Type == typeArgLenTarget { 319 comp.checkLenTarget(t, name, arg.Ident, fields, parents) 320 } else if argDesc.Type == typeArgType { 321 comp.checkLenType(arg, name, fields, parents, checked, argDesc.IsArg) 322 } 323 } 324} 325 326func (comp *compiler) checkLenTarget(t *ast.Type, name, target string, fields []*ast.Field, parents []*ast.Struct) { 327 if target == name { 328 comp.error(t.Pos, "%v target %v refer to itself", t.Ident, target) 329 return 330 } 331 if target == "parent" { 332 if len(parents) == 0 { 333 comp.error(t.Pos, "%v target %v does not exist", t.Ident, target) 334 } 335 return 336 } 337 for _, fld := range fields { 338 if target != fld.Name.Name { 339 continue 340 } 341 if fld.Type == t { 342 comp.error(t.Pos, "%v target %v refer to itself", t.Ident, target) 343 } 344 if t.Ident == "len" { 345 inner := fld.Type 346 desc, args, _ := comp.getArgsBase(inner, "", prog.DirIn, false) 347 for desc == typePtr { 348 if desc != typePtr { 349 break 350 } 351 inner = args[1] 352 desc, args, _ = comp.getArgsBase(inner, "", prog.DirIn, false) 353 } 354 if desc == typeArray && comp.isVarlen(args[0]) { 355 comp.warning(t.Pos, "len target %v refer to an array with"+ 356 " variable-size elements (do you mean bytesize?)", target) 357 } 358 } 359 return 360 } 361 for _, parent := range parents { 362 parentName := parent.Name.Name 363 if pos := strings.IndexByte(parentName, '['); pos != -1 { 364 // For template parents name is "struct_name[ARG1, ARG2]", strip the part after '['. 365 parentName = parentName[:pos] 366 } 367 if target == parentName { 368 return 369 } 370 } 371 comp.error(t.Pos, "%v target %v does not exist", t.Ident, target) 372} 373 374func (comp *compiler) collectUsed(all bool) (structs, flags, strflags map[string]bool) { 375 structs = make(map[string]bool) 376 flags = make(map[string]bool) 377 strflags = make(map[string]bool) 378 for _, decl := range comp.desc.Nodes { 379 switch n := decl.(type) { 380 case *ast.Call: 381 if !all && n.NR == ^uint64(0) { 382 break 383 } 384 for _, arg := range n.Args { 385 comp.collectUsedType(structs, flags, strflags, arg.Type, true) 386 } 387 if n.Ret != nil { 388 comp.collectUsedType(structs, flags, strflags, n.Ret, true) 389 } 390 } 391 } 392 return 393} 394 395func (comp *compiler) collectUsedType(structs, flags, strflags map[string]bool, t *ast.Type, isArg bool) { 396 desc := comp.getTypeDesc(t) 397 if desc == typeResource { 398 r := comp.resources[t.Ident] 399 for r != nil && !structs[r.Name.Name] { 400 structs[r.Name.Name] = true 401 r = comp.resources[r.Base.Ident] 402 } 403 return 404 } 405 if desc == typeStruct { 406 if structs[t.Ident] { 407 return 408 } 409 structs[t.Ident] = true 410 s := comp.structs[t.Ident] 411 for _, fld := range s.Fields { 412 comp.collectUsedType(structs, flags, strflags, fld.Type, false) 413 } 414 return 415 } 416 if desc == typeFlags { 417 flags[t.Args[0].Ident] = true 418 return 419 } 420 if desc == typeString { 421 if len(t.Args) != 0 && t.Args[0].Ident != "" { 422 strflags[t.Args[0].Ident] = true 423 } 424 return 425 } 426 _, args, _ := comp.getArgsBase(t, "", prog.DirIn, isArg) 427 for i, arg := range args { 428 if desc.Args[i].Type == typeArgType { 429 comp.collectUsedType(structs, flags, strflags, arg, desc.Args[i].IsArg) 430 } 431 } 432} 433 434func (comp *compiler) checkUnused() { 435 comp.used, _, _ = comp.collectUsed(false) 436 structs, flags, strflags := comp.collectUsed(true) 437 _, _, _ = structs, flags, strflags 438 439 for name, n := range comp.intFlags { 440 if !flags[name] { 441 comp.error(n.Pos, "unused flags %v", name) 442 } 443 } 444 for name, n := range comp.strFlags { 445 if !strflags[name] && builtinStrFlags[name] == nil { 446 comp.error(n.Pos, "unused string flags %v", name) 447 } 448 } 449 for name, n := range comp.resources { 450 if !structs[name] { 451 comp.error(n.Pos, "unused resource %v", name) 452 } 453 } 454 for name, n := range comp.structs { 455 if !structs[name] { 456 _, typ, _ := n.Info() 457 comp.error(n.Pos, "unused %v %v", typ, name) 458 } 459 } 460 for name, n := range comp.typedefs { 461 if !comp.usedTypedefs[name] { 462 comp.error(n.Pos, "unused type %v", name) 463 } 464 } 465} 466 467type structDir struct { 468 Struct string 469 Dir prog.Dir 470} 471 472func (comp *compiler) checkConstructors() { 473 ctors := make(map[string]bool) // resources for which we have ctors 474 checked := make(map[structDir]bool) 475 for _, decl := range comp.desc.Nodes { 476 switch n := decl.(type) { 477 case *ast.Call: 478 for _, arg := range n.Args { 479 comp.checkTypeCtors(arg.Type, prog.DirIn, true, ctors, checked) 480 } 481 if n.Ret != nil { 482 comp.checkTypeCtors(n.Ret, prog.DirOut, true, ctors, checked) 483 } 484 } 485 } 486 for _, decl := range comp.desc.Nodes { 487 switch n := decl.(type) { 488 case *ast.Resource: 489 name := n.Name.Name 490 if !ctors[name] && comp.used[name] { 491 comp.error(n.Pos, "resource %v can't be created"+ 492 " (never mentioned as a syscall return value or output argument/field)", 493 name) 494 } 495 } 496 } 497} 498 499func (comp *compiler) checkTypeCtors(t *ast.Type, dir prog.Dir, isArg bool, 500 ctors map[string]bool, checked map[structDir]bool) { 501 desc := comp.getTypeDesc(t) 502 if desc == typeResource { 503 // TODO(dvyukov): consider changing this to "dir == prog.DirOut". 504 // We have few questionable cases where resources can be created 505 // only by inout struct fields. These structs should be split 506 // into two different structs: one is in and second is out. 507 // But that will require attaching dir to individual fields. 508 if dir != prog.DirIn { 509 r := comp.resources[t.Ident] 510 for r != nil && !ctors[r.Name.Name] { 511 ctors[r.Name.Name] = true 512 r = comp.resources[r.Base.Ident] 513 } 514 } 515 return 516 } 517 if desc == typeStruct { 518 s := comp.structs[t.Ident] 519 name := s.Name.Name 520 key := structDir{name, dir} 521 if checked[key] { 522 return 523 } 524 checked[key] = true 525 for _, fld := range s.Fields { 526 comp.checkTypeCtors(fld.Type, dir, false, ctors, checked) 527 } 528 return 529 } 530 if desc == typePtr { 531 dir = genDir(t.Args[0]) 532 } 533 _, args, _ := comp.getArgsBase(t, "", dir, isArg) 534 for i, arg := range args { 535 if desc.Args[i].Type == typeArgType { 536 comp.checkTypeCtors(arg, dir, desc.Args[i].IsArg, ctors, checked) 537 } 538 } 539} 540 541func (comp *compiler) checkRecursion() { 542 checked := make(map[string]bool) 543 for _, decl := range comp.desc.Nodes { 544 switch n := decl.(type) { 545 case *ast.Resource: 546 comp.checkResourceRecursion(n) 547 case *ast.Struct: 548 var path []pathElem 549 comp.checkStructRecursion(checked, n, path) 550 } 551 } 552} 553 554func (comp *compiler) checkResourceRecursion(n *ast.Resource) { 555 var seen []string 556 for n != nil { 557 if arrayContains(seen, n.Name.Name) { 558 chain := "" 559 for _, r := range seen { 560 chain += r + "->" 561 } 562 chain += n.Name.Name 563 comp.error(n.Pos, "recursive resource %v", chain) 564 return 565 } 566 seen = append(seen, n.Name.Name) 567 n = comp.resources[n.Base.Ident] 568 } 569} 570 571type pathElem struct { 572 Pos ast.Pos 573 Struct string 574 Field string 575} 576 577func (comp *compiler) checkStructRecursion(checked map[string]bool, n *ast.Struct, path []pathElem) { 578 name := n.Name.Name 579 if checked[name] { 580 return 581 } 582 for i, elem := range path { 583 if elem.Struct != name { 584 continue 585 } 586 path = path[i:] 587 str := "" 588 for _, elem := range path { 589 str += fmt.Sprintf("%v.%v -> ", elem.Struct, elem.Field) 590 } 591 str += name 592 comp.error(path[0].Pos, "recursive declaration: %v (mark some pointers as opt)", str) 593 checked[name] = true 594 return 595 } 596 for _, f := range n.Fields { 597 path = append(path, pathElem{ 598 Pos: f.Pos, 599 Struct: name, 600 Field: f.Name.Name, 601 }) 602 comp.recurseField(checked, f.Type, path) 603 path = path[:len(path)-1] 604 } 605 checked[name] = true 606} 607 608func (comp *compiler) recurseField(checked map[string]bool, t *ast.Type, path []pathElem) { 609 desc := comp.getTypeDesc(t) 610 if desc == typeStruct { 611 comp.checkStructRecursion(checked, comp.structs[t.Ident], path) 612 return 613 } 614 _, args, base := comp.getArgsBase(t, "", prog.DirIn, false) 615 if desc == typePtr && base.IsOptional { 616 return // optional pointers prune recursion 617 } 618 for i, arg := range args { 619 if desc.Args[i].Type == typeArgType { 620 comp.recurseField(checked, arg, path) 621 } 622 } 623} 624 625func (comp *compiler) checkStruct(ctx checkCtx, n *ast.Struct) { 626 var flags checkFlags 627 if !n.IsUnion { 628 flags |= checkIsStruct 629 } 630 for _, f := range n.Fields { 631 comp.checkType(ctx, f.Type, flags) 632 } 633 for _, attr := range n.Attrs { 634 if unexpected, _, ok := checkTypeKind(attr, kindIdent); !ok { 635 comp.error(attr.Pos, "unexpected %v, expect attribute", unexpected) 636 return 637 } 638 if attr.HasColon { 639 comp.error(attr.Pos2, "unexpected ':'") 640 return 641 } 642 } 643 if n.IsUnion { 644 comp.parseUnionAttrs(n) 645 } else { 646 comp.parseStructAttrs(n) 647 } 648} 649 650type checkFlags int 651 652const ( 653 checkIsArg checkFlags = 1 << iota // immediate syscall arg type 654 checkIsRet // immediate syscall ret type 655 checkIsStruct // immediate struct field type 656 checkIsResourceBase // immediate resource base type 657 checkIsTypedef // immediate type alias/template type 658) 659 660type checkCtx struct { 661 instantiationStack []string 662} 663 664func (comp *compiler) checkType(ctx checkCtx, t *ast.Type, flags checkFlags) { 665 if unexpected, _, ok := checkTypeKind(t, kindIdent); !ok { 666 comp.error(t.Pos, "unexpected %v, expect type", unexpected) 667 return 668 } 669 desc := comp.getTypeDesc(t) 670 if desc == nil { 671 comp.error(t.Pos, "unknown type %v", t.Ident) 672 return 673 } 674 if desc == typeTypedef { 675 err0 := comp.errors 676 // Replace t with type alias/template target type inplace, 677 // and check the replaced type recursively. 678 comp.replaceTypedef(&ctx, t, flags) 679 if err0 == comp.errors { 680 comp.checkType(ctx, t, flags) 681 } 682 return 683 } 684 err0 := comp.errors 685 comp.checkTypeBasic(t, desc, flags) 686 if err0 != comp.errors { 687 return 688 } 689 args := comp.checkTypeArgs(t, desc, flags) 690 if err0 != comp.errors { 691 return 692 } 693 for i, arg := range args { 694 if desc.Args[i].Type == typeArgType { 695 var innerFlags checkFlags 696 if desc.Args[i].IsArg { 697 innerFlags |= checkIsArg 698 } 699 comp.checkType(ctx, arg, innerFlags) 700 } else { 701 comp.checkTypeArg(t, arg, desc.Args[i]) 702 } 703 } 704 if err0 != comp.errors { 705 return 706 } 707 if desc.Check != nil { 708 _, args, base := comp.getArgsBase(t, "", prog.DirIn, flags&checkIsArg != 0) 709 desc.Check(comp, t, args, base) 710 } 711} 712 713func (comp *compiler) checkTypeBasic(t *ast.Type, desc *typeDesc, flags checkFlags) { 714 if t.HasColon { 715 if !desc.AllowColon { 716 comp.error(t.Pos2, "unexpected ':'") 717 return 718 } 719 if flags&checkIsStruct == 0 { 720 comp.error(t.Pos2, "unexpected ':', only struct fields can be bitfields") 721 return 722 } 723 } 724 if flags&checkIsTypedef != 0 && !desc.CanBeTypedef { 725 comp.error(t.Pos, "%v can't be type alias target", t.Ident) 726 return 727 } 728 if flags&checkIsResourceBase != 0 && !desc.ResourceBase { 729 comp.error(t.Pos, "%v can't be resource base (int types can)", t.Ident) 730 return 731 } 732 canBeArg, canBeRet := false, false 733 if desc.CanBeArgRet != nil { 734 canBeArg, canBeRet = desc.CanBeArgRet(comp, t) 735 } 736 if flags&checkIsRet != 0 && !canBeRet { 737 comp.error(t.Pos, "%v can't be syscall return", t.Ident) 738 return 739 } 740 if flags&checkIsArg != 0 && !canBeArg { 741 comp.error(t.Pos, "%v can't be syscall argument", t.Ident) 742 return 743 } 744} 745 746func (comp *compiler) checkTypeArgs(t *ast.Type, desc *typeDesc, flags checkFlags) []*ast.Type { 747 args, opt := removeOpt(t) 748 if opt != nil { 749 if len(opt.Args) != 0 { 750 comp.error(opt.Pos, "opt can't have arguments") 751 } 752 if flags&checkIsResourceBase != 0 || desc.CantBeOpt { 753 what := "resource base" 754 if desc.CantBeOpt { 755 what = t.Ident 756 } 757 comp.error(opt.Pos, "%v can't be marked as opt", what) 758 return nil 759 } 760 } 761 addArgs := 0 762 needBase := flags&checkIsArg == 0 && desc.NeedBase 763 if needBase { 764 addArgs++ // last arg must be base type, e.g. const[0, int32] 765 } 766 if len(args) > len(desc.Args)+addArgs || len(args) < len(desc.Args)-desc.OptArgs+addArgs { 767 comp.error(t.Pos, "wrong number of arguments for type %v, expect %v", 768 t.Ident, expectedTypeArgs(desc, needBase)) 769 return nil 770 } 771 if needBase { 772 base := args[len(args)-1] 773 args = args[:len(args)-1] 774 comp.checkTypeArg(t, base, typeArgBase) 775 } 776 return args 777} 778 779func (comp *compiler) replaceTypedef(ctx *checkCtx, t *ast.Type, flags checkFlags) { 780 typedefName := t.Ident 781 comp.usedTypedefs[typedefName] = true 782 if t.HasColon { 783 comp.error(t.Pos, "type alias %v with ':'", t.Ident) 784 return 785 } 786 typedef := comp.typedefs[typedefName] 787 fullTypeName := ast.SerializeNode(t) 788 for i, prev := range ctx.instantiationStack { 789 if prev == fullTypeName { 790 ctx.instantiationStack = append(ctx.instantiationStack, fullTypeName) 791 path := "" 792 for j := i; j < len(ctx.instantiationStack); j++ { 793 if j != i { 794 path += " -> " 795 } 796 path += ctx.instantiationStack[j] 797 } 798 comp.error(t.Pos, "type instantiation loop: %v", path) 799 return 800 } 801 } 802 ctx.instantiationStack = append(ctx.instantiationStack, fullTypeName) 803 nargs := len(typedef.Args) 804 args := t.Args 805 if nargs != len(t.Args) { 806 if nargs == 0 { 807 comp.error(t.Pos, "type %v is not a template", typedefName) 808 } else { 809 comp.error(t.Pos, "template %v needs %v arguments instead of %v", 810 typedefName, nargs, len(t.Args)) 811 } 812 return 813 } 814 pos0 := t.Pos 815 if typedef.Type != nil { 816 *t = *typedef.Type.Clone().(*ast.Type) 817 if !comp.instantiate(t, typedef.Args, args) { 818 return 819 } 820 } else { 821 if comp.structs[fullTypeName] == nil { 822 inst := typedef.Struct.Clone().(*ast.Struct) 823 inst.Name.Name = fullTypeName 824 if !comp.instantiate(inst, typedef.Args, args) { 825 return 826 } 827 comp.checkStruct(*ctx, inst) 828 comp.desc.Nodes = append(comp.desc.Nodes, inst) 829 comp.structs[fullTypeName] = inst 830 } 831 *t = ast.Type{ 832 Ident: fullTypeName, 833 } 834 } 835 t.Pos = pos0 836 837 // Remove base type if it's not needed in this context. 838 desc := comp.getTypeDesc(t) 839 if flags&checkIsArg != 0 && desc.NeedBase { 840 baseTypePos := len(t.Args) - 1 841 if t.Args[baseTypePos].Ident == "opt" { 842 baseTypePos-- 843 } 844 copy(t.Args[baseTypePos:], t.Args[baseTypePos+1:]) 845 t.Args = t.Args[:len(t.Args)-1] 846 } 847} 848 849func (comp *compiler) instantiate(templ ast.Node, params []*ast.Ident, args []*ast.Type) bool { 850 if len(params) == 0 { 851 return true 852 } 853 argMap := make(map[string]*ast.Type) 854 for i, param := range params { 855 argMap[param.Name] = args[i] 856 } 857 err0 := comp.errors 858 templ.Walk(ast.Recursive(func(n ast.Node) { 859 templArg, ok := n.(*ast.Type) 860 if !ok { 861 return 862 } 863 if concreteArg := argMap[templArg.Ident]; concreteArg != nil { 864 origArgs := templArg.Args 865 if len(origArgs) != 0 && len(concreteArg.Args) != 0 { 866 comp.error(templArg.Pos, "both template parameter %v and its usage"+ 867 " have sub-arguments", templArg.Ident) 868 return 869 } 870 *templArg = *concreteArg.Clone().(*ast.Type) 871 if len(origArgs) != 0 { 872 templArg.Args = origArgs 873 } 874 } 875 // TODO(dvyukov): somewhat hacky, but required for int8[0:CONST_ARG] 876 // Need more checks here. E.g. that CONST_ARG does not have subargs. 877 // And if CONST_ARG is a value, then use concreteArg.Value. 878 // Also need to error if CONST_ARG is a string. 879 if concreteArg := argMap[templArg.Ident2]; concreteArg != nil { 880 templArg.Ident2 = concreteArg.Ident 881 templArg.Pos2 = concreteArg.Pos 882 } 883 })) 884 return err0 == comp.errors 885} 886 887func (comp *compiler) checkTypeArg(t, arg *ast.Type, argDesc namedArg) { 888 desc := argDesc.Type 889 if len(desc.Names) != 0 { 890 if unexpected, _, ok := checkTypeKind(arg, kindIdent); !ok { 891 comp.error(arg.Pos, "unexpected %v for %v argument of %v type, expect %+v", 892 unexpected, argDesc.Name, t.Ident, desc.Names) 893 return 894 } 895 if !arrayContains(desc.Names, arg.Ident) { 896 comp.error(arg.Pos, "unexpected value %v for %v argument of %v type, expect %+v", 897 arg.Ident, argDesc.Name, t.Ident, desc.Names) 898 return 899 } 900 } else { 901 if unexpected, expect, ok := checkTypeKind(arg, desc.Kind); !ok { 902 comp.error(arg.Pos, "unexpected %v for %v argument of %v type, expect %v", 903 unexpected, argDesc.Name, t.Ident, expect) 904 return 905 } 906 } 907 if !desc.AllowColon && arg.HasColon { 908 comp.error(arg.Pos2, "unexpected ':'") 909 return 910 } 911 if len(arg.Args) > desc.MaxArgs { 912 comp.error(arg.Pos, "%v argument has subargs", argDesc.Name) 913 return 914 } 915 if desc.Check != nil { 916 desc.Check(comp, arg) 917 } 918} 919 920func expectedTypeArgs(desc *typeDesc, needBase bool) string { 921 expect := "" 922 for i, arg := range desc.Args { 923 if expect != "" { 924 expect += ", " 925 } 926 opt := i >= len(desc.Args)-desc.OptArgs 927 if opt { 928 expect += "[" 929 } 930 expect += arg.Name 931 if opt { 932 expect += "]" 933 } 934 } 935 if needBase { 936 if expect != "" { 937 expect += ", " 938 } 939 expect += typeArgBase.Name 940 } 941 if !desc.CantBeOpt { 942 if expect != "" { 943 expect += ", " 944 } 945 expect += "[opt]" 946 } 947 if expect == "" { 948 expect = "no arguments" 949 } 950 return expect 951} 952 953func checkTypeKind(t *ast.Type, kind int) (unexpected string, expect string, ok bool) { 954 switch { 955 case kind == kindAny: 956 ok = true 957 case t.HasString: 958 ok = kind == kindString 959 if !ok { 960 unexpected = fmt.Sprintf("string %q", t.String) 961 } 962 case t.Ident != "": 963 ok = kind == kindIdent || kind == kindInt 964 if !ok { 965 unexpected = fmt.Sprintf("identifier %v", t.Ident) 966 } 967 default: 968 ok = kind == kindInt 969 if !ok { 970 unexpected = fmt.Sprintf("int %v", t.Value) 971 } 972 } 973 if !ok { 974 switch kind { 975 case kindString: 976 expect = "string" 977 case kindIdent: 978 expect = "identifier" 979 case kindInt: 980 expect = "int" 981 } 982 } 983 return 984} 985 986func (comp *compiler) checkVarlens() { 987 for _, decl := range comp.desc.Nodes { 988 switch n := decl.(type) { 989 case *ast.Struct: 990 comp.checkVarlen(n) 991 } 992 } 993} 994 995func (comp *compiler) isVarlen(t *ast.Type) bool { 996 desc, args, _ := comp.getArgsBase(t, "", prog.DirIn, false) 997 return desc.Varlen != nil && desc.Varlen(comp, t, args) 998} 999 1000func (comp *compiler) isZeroSize(t *ast.Type) bool { 1001 desc, args, _ := comp.getArgsBase(t, "", prog.DirIn, false) 1002 return desc.ZeroSize != nil && desc.ZeroSize(comp, t, args) 1003} 1004 1005func (comp *compiler) checkVarlen(n *ast.Struct) { 1006 // Non-varlen unions can't have varlen fields. 1007 // Non-packed structs can't have varlen fields in the middle. 1008 if n.IsUnion { 1009 if varlen, _ := comp.parseUnionAttrs(n); varlen { 1010 return 1011 } 1012 } else { 1013 if packed, _, _ := comp.parseStructAttrs(n); packed { 1014 return 1015 } 1016 } 1017 for i, f := range n.Fields { 1018 if !n.IsUnion && i == len(n.Fields)-1 { 1019 break 1020 } 1021 if comp.isVarlen(f.Type) { 1022 if n.IsUnion { 1023 comp.error(f.Pos, "variable size field %v in non-varlen union %v", 1024 f.Name.Name, n.Name.Name) 1025 } else { 1026 comp.error(f.Pos, "variable size field %v in the middle of non-packed struct %v", 1027 f.Name.Name, n.Name.Name) 1028 } 1029 } 1030 } 1031} 1032 1033func (comp *compiler) checkDupConsts() { 1034 // The idea is to detect copy-paste errors in const arguments, e.g.: 1035 // call$FOO(fd fd, arg const[FOO]) 1036 // call$BAR(fd fd, arg const[FOO]) 1037 // The second one is meant to be const[BAR], 1038 // Unfortunately, this does not fully work as it detects lots of false positives. 1039 // But was useful to find real bugs as well. So for now it's disabled, but can be run manually. 1040 if true { 1041 return 1042 } 1043 dups := make(map[string]map[string]dupConstArg) 1044 for _, decl := range comp.desc.Nodes { 1045 switch n := decl.(type) { 1046 case *ast.Call: 1047 comp.checkDupConstsCall(n, dups) 1048 } 1049 } 1050} 1051 1052type dupConstArg struct { 1053 pos ast.Pos 1054 name string 1055} 1056 1057func (comp *compiler) checkDupConstsCall(n *ast.Call, dups map[string]map[string]dupConstArg) { 1058 if n.NR == ^uint64(0) { 1059 return 1060 } 1061 for dups[n.CallName] == nil { 1062 dups[n.CallName] = make(map[string]dupConstArg) 1063 } 1064 hasConsts := false 1065 constArgID := "" 1066 for i, arg := range n.Args { 1067 desc := comp.getTypeDesc(arg.Type) 1068 if desc == typeConst { 1069 v := arg.Type.Args[0].Value 1070 if v != 0 && v != 18446744073709551516 { // AT_FDCWD 1071 constArgID += fmt.Sprintf("(%v-%v)", i, fmt.Sprintf("%v", v)) 1072 hasConsts = true 1073 } 1074 } else if desc == typeResource { 1075 constArgID += fmt.Sprintf("(%v-%v)", i, arg.Type.Ident) 1076 } 1077 } 1078 if !hasConsts { 1079 return 1080 } 1081 dup, ok := dups[n.CallName][constArgID] 1082 if !ok { 1083 dups[n.CallName][constArgID] = dupConstArg{ 1084 pos: n.Pos, 1085 name: n.Name.Name, 1086 } 1087 return 1088 } 1089 comp.error(n.Pos, "call %v: duplicate const %v, previously used in call %v at %v", 1090 n.Name.Name, constArgID, dup.name, dup.pos) 1091} 1092