github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/compiler/consts.go (about)

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