github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/prog/target.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 prog
     5  
     6  import (
     7  	"fmt"
     8  	"math/rand"
     9  	"sort"
    10  	"strings"
    11  	"sync"
    12  	"sync/atomic"
    13  )
    14  
    15  // Target describes target OS/arch pair.
    16  type Target struct {
    17  	OS                string
    18  	Arch              string
    19  	Revision          string // unique hash representing revision of the descriptions
    20  	PtrSize           uint64
    21  	PageSize          uint64
    22  	NumPages          uint64
    23  	DataOffset        uint64
    24  	LittleEndian      bool
    25  	ExecutorUsesShmem bool
    26  
    27  	Syscalls  []*Syscall
    28  	Resources []*ResourceDesc
    29  	Consts    []ConstValue
    30  	Flags     []FlagDesc
    31  
    32  	// MakeDataMmap creates calls that mmaps target data memory range.
    33  	MakeDataMmap func() []*Call
    34  
    35  	// Neutralize neutralizes harmful calls by transforming them into non-harmful ones
    36  	// (e.g. an ioctl that turns off console output is turned into ioctl that turns on output).
    37  	// fixStructure determines whether it's allowed to make structural changes (e.g. add or
    38  	// remove arguments). It is helpful e.g. when we do neutralization while iterating over the
    39  	// arguments.
    40  	Neutralize func(c *Call, fixStructure bool) error
    41  
    42  	// AnnotateCall annotates a syscall invocation in C reproducers.
    43  	// The returned string will be placed inside a comment except for the
    44  	// empty string which will omit the comment.
    45  	AnnotateCall func(c ExecCall) string
    46  
    47  	// SpecialTypes allows target to do custom generation/mutation for some struct's and union's.
    48  	// Map key is struct/union name for which custom generation/mutation is required.
    49  	// Map value is custom generation/mutation function that will be called
    50  	// for the corresponding type. g is helper object that allows generate random numbers,
    51  	// allocate memory, etc. typ is the struct/union type. old is the old value of the struct/union
    52  	// for mutation, or nil for generation. The function returns a new value of the struct/union,
    53  	// and optionally any calls that need to be inserted before the arg reference.
    54  	SpecialTypes map[string]func(g *Gen, typ Type, dir Dir, old Arg) (Arg, []*Call)
    55  
    56  	// Resources that play auxiliary role, but widely used throughout all syscalls (e.g. pid/uid).
    57  	AuxResources map[string]bool
    58  
    59  	// Additional special invalid pointer values besides NULL to use.
    60  	SpecialPointers []uint64
    61  
    62  	// Special file name length that can provoke bugs (e.g. PATH_MAX).
    63  	SpecialFileLenghts []int
    64  
    65  	// Filled by prog package:
    66  	SyscallMap map[string]*Syscall
    67  	ConstMap   map[string]uint64
    68  	FlagsMap   map[string][]string
    69  
    70  	init        sync.Once
    71  	initArch    func(target *Target)
    72  	types       []Type
    73  	resourceMap map[string]*ResourceDesc
    74  	// Maps resource name to a list of calls that can create the resource.
    75  	resourceCtors map[string][]ResourceCtor
    76  	any           anyTypes
    77  
    78  	// The default ChoiceTable is used only by tests and utilities, so we initialize it lazily.
    79  	defaultOnce        sync.Once
    80  	defaultChoiceTable *ChoiceTable
    81  }
    82  
    83  const maxSpecialPointers = 16
    84  
    85  var targets = make(map[string]*Target)
    86  
    87  func RegisterTarget(target *Target, types []Type, initArch func(target *Target)) {
    88  	key := target.OS + "/" + target.Arch
    89  	if targets[key] != nil {
    90  		panic(fmt.Sprintf("duplicate target %v", key))
    91  	}
    92  	target.initArch = initArch
    93  	target.types = types
    94  	targets[key] = target
    95  }
    96  
    97  func GetTarget(OS, arch string) (*Target, error) {
    98  	if OS == "android" {
    99  		OS = "linux"
   100  	}
   101  	key := OS + "/" + arch
   102  	target := targets[key]
   103  	if target == nil {
   104  		var supported []string
   105  		for _, t := range targets {
   106  			supported = append(supported, fmt.Sprintf("%v/%v", t.OS, t.Arch))
   107  		}
   108  		sort.Strings(supported)
   109  		return nil, fmt.Errorf("unknown target: %v (supported: %v)", key, supported)
   110  	}
   111  	target.init.Do(target.lazyInit)
   112  	return target, nil
   113  }
   114  
   115  func AllTargets() []*Target {
   116  	var res []*Target
   117  	for _, target := range targets {
   118  		target.init.Do(target.lazyInit)
   119  		res = append(res, target)
   120  	}
   121  	sort.Slice(res, func(i, j int) bool {
   122  		if res[i].OS != res[j].OS {
   123  			return res[i].OS < res[j].OS
   124  		}
   125  		return res[i].Arch < res[j].Arch
   126  	})
   127  	return res
   128  }
   129  
   130  func (target *Target) lazyInit() {
   131  	target.Neutralize = func(c *Call, fixStructure bool) error { return nil }
   132  	target.AnnotateCall = func(c ExecCall) string { return "" }
   133  	target.initTarget()
   134  	target.initArch(target)
   135  	// Give these 2 known addresses fixed positions and prepend target-specific ones at the end.
   136  	target.SpecialPointers = append([]uint64{
   137  		0x0000000000000000, // NULL pointer (keep this first because code uses special index=0 as NULL)
   138  		0xffffffffffffffff, // unmapped kernel address (keep second because serialized value will match actual pointer value)
   139  		0x9999999999999999, // non-canonical address
   140  	}, target.SpecialPointers...)
   141  	if len(target.SpecialPointers) > maxSpecialPointers {
   142  		panic("too many special pointers")
   143  	}
   144  	if len(target.SpecialFileLenghts) == 0 {
   145  		// Just some common lengths that can be used as PATH_MAX/MAX_NAME.
   146  		target.SpecialFileLenghts = []int{256, 512, 4096}
   147  	}
   148  	for _, ln := range target.SpecialFileLenghts {
   149  		if ln <= 0 || ln >= memAllocMaxMem {
   150  			panic(fmt.Sprintf("bad special file length %v", ln))
   151  		}
   152  	}
   153  	// These are used only during lazyInit.
   154  	target.types = nil
   155  }
   156  
   157  func (target *Target) initTarget() {
   158  	checkMaxCallID(len(target.Syscalls) - 1)
   159  	target.ConstMap = make(map[string]uint64)
   160  	for _, c := range target.Consts {
   161  		target.ConstMap[c.Name] = c.Value
   162  	}
   163  
   164  	target.resourceMap = restoreLinks(target.Syscalls, target.Resources, target.types)
   165  	target.initAnyTypes()
   166  
   167  	target.SyscallMap = make(map[string]*Syscall)
   168  	for i, c := range target.Syscalls {
   169  		c.ID = i
   170  		target.SyscallMap[c.Name] = c
   171  	}
   172  
   173  	target.FlagsMap = make(map[string][]string)
   174  	for _, c := range target.Flags {
   175  		target.FlagsMap[c.Name] = c.Values
   176  	}
   177  
   178  	target.populateResourceCtors()
   179  	target.resourceCtors = make(map[string][]ResourceCtor)
   180  	for _, res := range target.Resources {
   181  		target.resourceCtors[res.Name] = target.calcResourceCtors(res, false)
   182  	}
   183  }
   184  
   185  func (target *Target) GetConst(name string) uint64 {
   186  	v, ok := target.ConstMap[name]
   187  	if !ok {
   188  		panic(fmt.Sprintf("const %v is not defined for %v/%v", name, target.OS, target.Arch))
   189  	}
   190  	return v
   191  }
   192  
   193  func (target *Target) sanitize(c *Call, fix bool) error {
   194  	// For now, even though we accept the fix argument, it does not have the full effect.
   195  	// It de facto only denies structural changes, e.g. deletions of arguments.
   196  	// TODO: rewrite the corresponding sys/*/init.go code.
   197  	return target.Neutralize(c, fix)
   198  }
   199  
   200  func RestoreLinks(syscalls []*Syscall, resources []*ResourceDesc, types []Type) {
   201  	restoreLinks(syscalls, resources, types)
   202  }
   203  
   204  var (
   205  	typeRefMu sync.Mutex
   206  	typeRefs  atomic.Value // []Type
   207  )
   208  
   209  func restoreLinks(syscalls []*Syscall, resources []*ResourceDesc, types []Type) map[string]*ResourceDesc {
   210  	typeRefMu.Lock()
   211  	defer typeRefMu.Unlock()
   212  	refs := []Type{nil}
   213  	if old := typeRefs.Load(); old != nil {
   214  		refs = old.([]Type)
   215  	}
   216  	for _, typ := range types {
   217  		typ.setRef(Ref(len(refs)))
   218  		refs = append(refs, typ)
   219  	}
   220  	typeRefs.Store(refs)
   221  
   222  	resourceMap := make(map[string]*ResourceDesc)
   223  	for _, res := range resources {
   224  		resourceMap[res.Name] = res
   225  	}
   226  
   227  	ForeachType(syscalls, func(typ Type, ctx *TypeCtx) {
   228  		if ref, ok := typ.(Ref); ok {
   229  			typ = types[ref]
   230  			*ctx.Ptr = typ
   231  		}
   232  		switch t := typ.(type) {
   233  		case *ResourceType:
   234  			t.Desc = resourceMap[t.TypeName]
   235  			if t.Desc == nil {
   236  				panic("no resource desc")
   237  			}
   238  		}
   239  	})
   240  	return resourceMap
   241  }
   242  
   243  func (target *Target) DefaultChoiceTable() *ChoiceTable {
   244  	target.defaultOnce.Do(func() {
   245  		target.defaultChoiceTable = target.BuildChoiceTable(nil, nil)
   246  	})
   247  	return target.defaultChoiceTable
   248  }
   249  
   250  func (target *Target) RequiredGlobs() []string {
   251  	globs := make(map[string]bool)
   252  	ForeachType(target.Syscalls, func(typ Type, ctx *TypeCtx) {
   253  		switch a := typ.(type) {
   254  		case *BufferType:
   255  			if a.Kind == BufferGlob {
   256  				for _, glob := range requiredGlobs(a.SubKind) {
   257  					globs[glob] = true
   258  				}
   259  			}
   260  		}
   261  	})
   262  	return stringMapToSlice(globs)
   263  }
   264  
   265  func (target *Target) UpdateGlobs(globFiles map[string][]string) {
   266  	// TODO: make host.DetectSupportedSyscalls below filter out globs with no values.
   267  	// Also make prog package more strict with respect to generation/mutation of globs
   268  	// with no values (they still can appear in tests and tools). We probably should
   269  	// generate an empty string for these and never mutate.
   270  	ForeachType(target.Syscalls, func(typ Type, ctx *TypeCtx) {
   271  		switch a := typ.(type) {
   272  		case *BufferType:
   273  			if a.Kind == BufferGlob {
   274  				a.Values = populateGlob(a.SubKind, globFiles)
   275  			}
   276  		}
   277  	})
   278  }
   279  
   280  func requiredGlobs(pattern string) []string {
   281  	var res []string
   282  	for _, tok := range strings.Split(pattern, ":") {
   283  		if tok[0] != '-' {
   284  			res = append(res, tok)
   285  		}
   286  	}
   287  	return res
   288  }
   289  
   290  func populateGlob(pattern string, globFiles map[string][]string) []string {
   291  	files := make(map[string]bool)
   292  	parts := strings.Split(pattern, ":")
   293  	for _, tok := range parts {
   294  		if tok[0] != '-' {
   295  			for _, file := range globFiles[tok] {
   296  				files[file] = true
   297  			}
   298  		}
   299  	}
   300  	for _, tok := range parts {
   301  		if tok[0] == '-' {
   302  			delete(files, tok[1:])
   303  		}
   304  	}
   305  	return stringMapToSlice(files)
   306  }
   307  
   308  func stringMapToSlice(m map[string]bool) []string {
   309  	var res []string
   310  	for k := range m {
   311  		res = append(res, k)
   312  	}
   313  	sort.Strings(res)
   314  	return res
   315  }
   316  
   317  type Gen struct {
   318  	r *randGen
   319  	s *state
   320  }
   321  
   322  func (g *Gen) Target() *Target {
   323  	return g.r.target
   324  }
   325  
   326  func (g *Gen) Rand() *rand.Rand {
   327  	return g.r.Rand
   328  }
   329  
   330  func (g *Gen) NOutOf(n, outOf int) bool {
   331  	return g.r.nOutOf(n, outOf)
   332  }
   333  
   334  func (g *Gen) Alloc(ptrType Type, dir Dir, data Arg) (Arg, []*Call) {
   335  	return g.r.allocAddr(g.s, ptrType, dir, data.Size(), data), nil
   336  }
   337  
   338  func (g *Gen) GenerateArg(typ Type, dir Dir, pcalls *[]*Call) Arg {
   339  	return g.generateArg(typ, dir, pcalls, false)
   340  }
   341  
   342  func (g *Gen) GenerateSpecialArg(typ Type, dir Dir, pcalls *[]*Call) Arg {
   343  	return g.generateArg(typ, dir, pcalls, true)
   344  }
   345  
   346  func (g *Gen) generateArg(typ Type, dir Dir, pcalls *[]*Call, ignoreSpecial bool) Arg {
   347  	arg, calls := g.r.generateArgImpl(g.s, typ, dir, ignoreSpecial)
   348  	*pcalls = append(*pcalls, calls...)
   349  	g.r.target.assignSizesArray([]Arg{arg}, []Field{{Name: "", Type: arg.Type()}}, nil)
   350  	return arg
   351  }
   352  
   353  func (g *Gen) MutateArg(arg0 Arg) (calls []*Call) {
   354  	updateSizes := true
   355  	for stop := false; !stop; stop = g.r.oneOf(3) {
   356  		ma := &mutationArgs{target: g.r.target, ignoreSpecial: true}
   357  		ForeachSubArg(arg0, ma.collectArg)
   358  		if len(ma.args) == 0 {
   359  			// TODO(dvyukov): probably need to return this condition
   360  			// and updateSizes to caller so that Mutate can act accordingly.
   361  			return
   362  		}
   363  		arg, ctx := ma.chooseArg(g.r.Rand)
   364  		newCalls, ok := g.r.target.mutateArg(g.r, g.s, arg, ctx, &updateSizes)
   365  		if !ok {
   366  			continue
   367  		}
   368  		calls = append(calls, newCalls...)
   369  	}
   370  	return calls
   371  }
   372  
   373  type Builder struct {
   374  	target *Target
   375  	ma     *memAlloc
   376  	p      *Prog
   377  }
   378  
   379  func MakeProgGen(target *Target) *Builder {
   380  	return &Builder{
   381  		target: target,
   382  		ma:     newMemAlloc(target.NumPages * target.PageSize),
   383  		p: &Prog{
   384  			Target: target,
   385  		},
   386  	}
   387  }
   388  
   389  func (pg *Builder) Append(c *Call) error {
   390  	pg.target.assignSizesCall(c)
   391  	pg.target.sanitize(c, true)
   392  	pg.p.Calls = append(pg.p.Calls, c)
   393  	return nil
   394  }
   395  
   396  func (pg *Builder) Allocate(size, alignment uint64) uint64 {
   397  	return pg.ma.alloc(nil, size, alignment)
   398  }
   399  
   400  func (pg *Builder) AllocateVMA(npages uint64) uint64 {
   401  	return pg.ma.alloc(nil, npages*pg.target.PageSize, pg.target.PageSize)
   402  }
   403  
   404  func (pg *Builder) Finalize() (*Prog, error) {
   405  	if err := pg.p.validate(); err != nil {
   406  		return nil, err
   407  	}
   408  	if _, err := pg.p.SerializeForExec(); err != nil {
   409  		return nil, err
   410  	}
   411  	p := pg.p
   412  	pg.p = nil
   413  	return p, nil
   414  }