2017-09-05 11:31:14 +00:00
|
|
|
// Copyright 2017 syzkaller project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package prog
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2017-11-22 10:42:10 +00:00
|
|
|
"math/rand"
|
2017-09-14 19:27:56 +00:00
|
|
|
"sort"
|
2017-12-13 10:58:10 +00:00
|
|
|
"sync"
|
2017-09-05 11:31:14 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Target describes target OS/arch pair.
|
|
|
|
type Target struct {
|
|
|
|
OS string
|
|
|
|
Arch string
|
2017-09-15 08:15:00 +00:00
|
|
|
Revision string // unique hash representing revision of the descriptions
|
2017-09-05 11:31:14 +00:00
|
|
|
PtrSize uint64
|
|
|
|
PageSize uint64
|
2018-02-19 18:35:04 +00:00
|
|
|
NumPages uint64
|
2017-09-05 11:31:14 +00:00
|
|
|
DataOffset uint64
|
|
|
|
|
|
|
|
Syscalls []*Syscall
|
|
|
|
Resources []*ResourceDesc
|
2017-09-20 19:18:36 +00:00
|
|
|
Consts []ConstValue
|
2020-04-25 08:06:37 +00:00
|
|
|
Types []Type
|
2017-09-05 11:31:14 +00:00
|
|
|
|
2020-04-18 10:36:52 +00:00
|
|
|
// MakeDataMmap creates calls that mmaps target data memory range.
|
|
|
|
MakeDataMmap func() []*Call
|
2017-09-05 11:31:14 +00:00
|
|
|
|
2020-03-14 15:42:00 +00:00
|
|
|
// Neutralize neutralizes harmful calls by transforming them into non-harmful ones
|
|
|
|
// (e.g. an ioctl that turns off console output is turned into ioctl that turns on output).
|
|
|
|
Neutralize func(c *Call)
|
2017-09-05 11:31:14 +00:00
|
|
|
|
pkg/csource: add ability to annotate syscalls using comments in C reproducers
Providing additional info, especially regarding syscall arguments, in reproducers
can be helpful. An example is device numbers passed to mknod(2).
This commit introduces an optional annotate function on a per target basis.
Example for the OpenBSD target:
$ cat prog.in
mknod(0x0, 0x0, 0x4503)
getpid()
$ syz-prog2c -prog prog.in
int main(void)
{
syscall(SYS_mmap, 0x20000000, 0x1000000, 3, 0x1012, -1, 0, 0);
syscall(SYS_mknod, 0, 0, 0x4503); /* major = 69, minor = 3 */
syscall(SYS_getpid);
return 0;
}
2019-05-21 21:17:22 +00:00
|
|
|
// AnnotateCall annotates a syscall invocation in C reproducers.
|
|
|
|
// The returned string will be placed inside a comment except for the
|
|
|
|
// empty string which will omit the comment.
|
|
|
|
AnnotateCall func(c ExecCall) string
|
|
|
|
|
2018-01-24 18:28:36 +00:00
|
|
|
// SpecialTypes allows target to do custom generation/mutation for some struct's and union's.
|
|
|
|
// Map key is struct/union name for which custom generation/mutation is required.
|
2017-09-05 11:31:14 +00:00
|
|
|
// Map value is custom generation/mutation function that will be called
|
2018-01-24 18:28:36 +00:00
|
|
|
// for the corresponding type. g is helper object that allows generate random numbers,
|
|
|
|
// allocate memory, etc. typ is the struct/union type. old is the old value of the struct/union
|
|
|
|
// for mutation, or nil for generation. The function returns a new value of the struct/union,
|
2017-09-05 11:31:14 +00:00
|
|
|
// and optionally any calls that need to be inserted before the arg reference.
|
2020-04-26 12:14:14 +00:00
|
|
|
SpecialTypes map[string]func(g *Gen, typ Type, dir Dir, old Arg) (Arg, []*Call)
|
2017-09-05 11:31:14 +00:00
|
|
|
|
2017-09-05 14:28:58 +00:00
|
|
|
// Special strings that can matter for the target.
|
|
|
|
// Used as fallback when string type does not have own dictionary.
|
|
|
|
StringDictionary []string
|
|
|
|
|
2019-12-30 10:41:20 +00:00
|
|
|
// Resources that play auxiliary role, but widely used throughout all syscalls (e.g. pid/uid).
|
|
|
|
AuxResources map[string]bool
|
|
|
|
|
2018-08-30 21:17:47 +00:00
|
|
|
// Additional special invalid pointer values besides NULL to use.
|
|
|
|
SpecialPointers []uint64
|
|
|
|
|
2017-09-14 17:25:01 +00:00
|
|
|
// Filled by prog package:
|
2017-12-13 10:58:10 +00:00
|
|
|
init sync.Once
|
|
|
|
initArch func(target *Target)
|
2017-09-14 17:25:01 +00:00
|
|
|
SyscallMap map[string]*Syscall
|
2017-09-20 19:18:36 +00:00
|
|
|
ConstMap map[string]uint64
|
2017-09-05 14:32:32 +00:00
|
|
|
resourceMap map[string]*ResourceDesc
|
|
|
|
// Maps resource name to a list of calls that can create the resource.
|
2017-09-05 11:31:14 +00:00
|
|
|
resourceCtors map[string][]*Syscall
|
2018-03-08 17:48:26 +00:00
|
|
|
any anyTypes
|
2020-05-04 06:58:32 +00:00
|
|
|
|
|
|
|
// The default ChoiceTable is used only by tests and utilities, so we initialize it lazily.
|
|
|
|
defaultOnce sync.Once
|
|
|
|
defaultChoiceTable *ChoiceTable
|
2017-09-05 11:31:14 +00:00
|
|
|
}
|
|
|
|
|
2018-08-30 21:17:47 +00:00
|
|
|
const maxSpecialPointers = 16
|
|
|
|
|
2017-09-05 11:31:14 +00:00
|
|
|
var targets = make(map[string]*Target)
|
|
|
|
|
2017-09-20 19:18:36 +00:00
|
|
|
func RegisterTarget(target *Target, initArch func(target *Target)) {
|
2017-09-05 11:31:14 +00:00
|
|
|
key := target.OS + "/" + target.Arch
|
|
|
|
if targets[key] != nil {
|
|
|
|
panic(fmt.Sprintf("duplicate target %v", key))
|
|
|
|
}
|
2017-12-13 10:58:10 +00:00
|
|
|
target.initArch = initArch
|
2017-09-05 11:31:14 +00:00
|
|
|
targets[key] = target
|
2017-09-13 13:15:36 +00:00
|
|
|
}
|
2017-09-05 11:31:14 +00:00
|
|
|
|
2017-09-14 17:25:01 +00:00
|
|
|
func GetTarget(OS, arch string) (*Target, error) {
|
2018-10-08 22:57:53 +00:00
|
|
|
if OS == "android" {
|
|
|
|
OS = "linux"
|
|
|
|
}
|
2017-09-13 13:15:36 +00:00
|
|
|
key := OS + "/" + arch
|
|
|
|
target := targets[key]
|
|
|
|
if target == nil {
|
2017-09-13 18:40:27 +00:00
|
|
|
var supported []string
|
|
|
|
for _, t := range targets {
|
|
|
|
supported = append(supported, fmt.Sprintf("%v/%v", t.OS, t.Arch))
|
|
|
|
}
|
2017-09-14 19:27:56 +00:00
|
|
|
sort.Strings(supported)
|
2017-09-14 17:25:01 +00:00
|
|
|
return nil, fmt.Errorf("unknown target: %v (supported: %v)", key, supported)
|
2017-09-05 11:31:14 +00:00
|
|
|
}
|
2017-12-13 10:58:10 +00:00
|
|
|
target.init.Do(target.lazyInit)
|
2017-09-14 17:25:01 +00:00
|
|
|
return target, nil
|
2017-09-05 11:31:14 +00:00
|
|
|
}
|
|
|
|
|
2017-09-14 19:27:56 +00:00
|
|
|
func AllTargets() []*Target {
|
|
|
|
var res []*Target
|
2017-12-13 10:58:10 +00:00
|
|
|
for _, target := range targets {
|
|
|
|
target.init.Do(target.lazyInit)
|
|
|
|
res = append(res, target)
|
2017-09-14 19:27:56 +00:00
|
|
|
}
|
|
|
|
sort.Slice(res, func(i, j int) bool {
|
|
|
|
if res[i].OS != res[j].OS {
|
|
|
|
return res[i].OS < res[j].OS
|
|
|
|
}
|
|
|
|
return res[i].Arch < res[j].Arch
|
|
|
|
})
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2017-12-13 10:58:10 +00:00
|
|
|
func (target *Target) lazyInit() {
|
2020-03-14 15:42:00 +00:00
|
|
|
target.Neutralize = func(c *Call) {}
|
pkg/csource: add ability to annotate syscalls using comments in C reproducers
Providing additional info, especially regarding syscall arguments, in reproducers
can be helpful. An example is device numbers passed to mknod(2).
This commit introduces an optional annotate function on a per target basis.
Example for the OpenBSD target:
$ cat prog.in
mknod(0x0, 0x0, 0x4503)
getpid()
$ syz-prog2c -prog prog.in
int main(void)
{
syscall(SYS_mmap, 0x20000000, 0x1000000, 3, 0x1012, -1, 0, 0);
syscall(SYS_mknod, 0, 0, 0x4503); /* major = 69, minor = 3 */
syscall(SYS_getpid);
return 0;
}
2019-05-21 21:17:22 +00:00
|
|
|
target.AnnotateCall = func(c ExecCall) string { return "" }
|
2017-12-13 10:58:10 +00:00
|
|
|
target.initTarget()
|
|
|
|
target.initArch(target)
|
|
|
|
target.ConstMap = nil // currently used only by initArch
|
2018-08-30 21:17:47 +00:00
|
|
|
// Give these 2 known addresses fixed positions and prepend target-specific ones at the end.
|
|
|
|
target.SpecialPointers = append([]uint64{
|
|
|
|
0x0000000000000000, // NULL pointer (keep this first because code uses special index=0 as NULL)
|
|
|
|
0xffffffffffffffff, // unmapped kernel address (keep second because serialized value will match actual pointer value)
|
|
|
|
0x9999999999999999, // non-canonical address
|
|
|
|
}, target.SpecialPointers...)
|
|
|
|
if len(target.SpecialPointers) > maxSpecialPointers {
|
|
|
|
panic("too many special pointers")
|
|
|
|
}
|
2017-12-13 10:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (target *Target) initTarget() {
|
2017-09-20 19:18:36 +00:00
|
|
|
target.ConstMap = make(map[string]uint64)
|
|
|
|
for _, c := range target.Consts {
|
|
|
|
target.ConstMap[c.Name] = c.Value
|
|
|
|
}
|
|
|
|
|
2020-05-03 09:29:12 +00:00
|
|
|
target.resourceMap = restoreLinks(target.Syscalls, target.Resources, target.Types)
|
2020-04-25 08:06:37 +00:00
|
|
|
target.Types = nil
|
2017-09-20 19:18:36 +00:00
|
|
|
|
2017-09-14 17:25:01 +00:00
|
|
|
target.SyscallMap = make(map[string]*Syscall)
|
2018-02-25 13:31:40 +00:00
|
|
|
for i, c := range target.Syscalls {
|
|
|
|
c.ID = i
|
2017-09-14 17:25:01 +00:00
|
|
|
target.SyscallMap[c.Name] = c
|
2019-12-30 10:41:20 +00:00
|
|
|
c.inputResources = target.getInputResources(c)
|
|
|
|
c.outputResources = target.getOutputResources(c)
|
2017-09-05 11:31:14 +00:00
|
|
|
}
|
2017-09-20 19:18:36 +00:00
|
|
|
|
2019-08-21 18:05:22 +00:00
|
|
|
target.populateResourceCtors()
|
2017-09-05 11:31:14 +00:00
|
|
|
target.resourceCtors = make(map[string][]*Syscall)
|
2017-09-20 19:18:36 +00:00
|
|
|
for _, res := range target.Resources {
|
2019-08-21 18:05:22 +00:00
|
|
|
target.resourceCtors[res.Name] = target.calcResourceCtors(res, false)
|
2017-09-05 11:31:14 +00:00
|
|
|
}
|
2018-02-24 13:33:36 +00:00
|
|
|
initAnyTypes(target)
|
2017-09-05 11:31:14 +00:00
|
|
|
}
|
|
|
|
|
2018-10-19 18:08:19 +00:00
|
|
|
func (target *Target) GetConst(name string) uint64 {
|
|
|
|
if target.ConstMap == nil {
|
|
|
|
panic("GetConst can only be used during target initialization")
|
|
|
|
}
|
|
|
|
v, ok := target.ConstMap[name]
|
|
|
|
if !ok {
|
|
|
|
panic(fmt.Sprintf("const %v is not defined for %v/%v", name, target.OS, target.Arch))
|
|
|
|
}
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2020-03-14 15:42:00 +00:00
|
|
|
func (target *Target) sanitize(c *Call, fix bool) error {
|
|
|
|
target.Neutralize(c)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-05-03 09:29:12 +00:00
|
|
|
func RestoreLinks(syscalls []*Syscall, resources []*ResourceDesc, types []Type) {
|
|
|
|
restoreLinks(syscalls, resources, types)
|
2019-12-17 16:34:47 +00:00
|
|
|
}
|
|
|
|
|
2020-05-03 09:29:12 +00:00
|
|
|
func restoreLinks(syscalls []*Syscall, resources []*ResourceDesc, types []Type) map[string]*ResourceDesc {
|
2019-12-17 16:34:47 +00:00
|
|
|
resourceMap := make(map[string]*ResourceDesc)
|
|
|
|
for _, res := range resources {
|
|
|
|
resourceMap[res.Name] = res
|
|
|
|
}
|
2020-05-03 09:29:12 +00:00
|
|
|
ForeachType(syscalls, func(_ Type, ctx TypeCtx) {
|
|
|
|
if ref, ok := (*ctx.Ptr).(Ref); ok {
|
|
|
|
*ctx.Ptr = types[ref]
|
2020-04-25 08:06:37 +00:00
|
|
|
}
|
2020-05-03 09:29:12 +00:00
|
|
|
switch t := (*ctx.Ptr).(type) {
|
|
|
|
case *ResourceType:
|
|
|
|
t.Desc = resourceMap[t.TypeName]
|
|
|
|
if t.Desc == nil {
|
|
|
|
panic("no resource desc")
|
2019-12-17 16:34:47 +00:00
|
|
|
}
|
2020-05-03 09:29:12 +00:00
|
|
|
}
|
|
|
|
})
|
2019-12-17 16:34:47 +00:00
|
|
|
return resourceMap
|
|
|
|
}
|
|
|
|
|
2020-05-04 06:58:32 +00:00
|
|
|
func (target *Target) DefaultChoiceTable() *ChoiceTable {
|
|
|
|
target.defaultOnce.Do(func() {
|
|
|
|
target.defaultChoiceTable = target.BuildChoiceTable(nil, nil)
|
|
|
|
})
|
|
|
|
return target.defaultChoiceTable
|
|
|
|
}
|
|
|
|
|
2017-09-05 11:31:14 +00:00
|
|
|
type Gen struct {
|
|
|
|
r *randGen
|
|
|
|
s *state
|
|
|
|
}
|
|
|
|
|
2018-02-17 16:17:56 +00:00
|
|
|
func (g *Gen) Target() *Target {
|
|
|
|
return g.r.target
|
|
|
|
}
|
|
|
|
|
2017-11-22 10:42:10 +00:00
|
|
|
func (g *Gen) Rand() *rand.Rand {
|
|
|
|
return g.r.Rand
|
|
|
|
}
|
|
|
|
|
2017-09-05 11:31:14 +00:00
|
|
|
func (g *Gen) NOutOf(n, outOf int) bool {
|
|
|
|
return g.r.nOutOf(n, outOf)
|
|
|
|
}
|
|
|
|
|
2020-04-26 12:14:14 +00:00
|
|
|
func (g *Gen) Alloc(ptrType Type, dir Dir, data Arg) (Arg, []*Call) {
|
|
|
|
return g.r.allocAddr(g.s, ptrType, dir, data.Size(), data), nil
|
2017-09-05 11:31:14 +00:00
|
|
|
}
|
2017-11-22 10:42:10 +00:00
|
|
|
|
2020-04-26 12:14:14 +00:00
|
|
|
func (g *Gen) GenerateArg(typ Type, dir Dir, pcalls *[]*Call) Arg {
|
|
|
|
return g.generateArg(typ, dir, pcalls, false)
|
2018-01-24 18:28:36 +00:00
|
|
|
}
|
|
|
|
|
2020-04-26 12:14:14 +00:00
|
|
|
func (g *Gen) GenerateSpecialArg(typ Type, dir Dir, pcalls *[]*Call) Arg {
|
|
|
|
return g.generateArg(typ, dir, pcalls, true)
|
2018-01-24 18:28:36 +00:00
|
|
|
}
|
|
|
|
|
2020-04-26 12:14:14 +00:00
|
|
|
func (g *Gen) generateArg(typ Type, dir Dir, pcalls *[]*Call, ignoreSpecial bool) Arg {
|
|
|
|
arg, calls := g.r.generateArgImpl(g.s, typ, dir, ignoreSpecial)
|
2017-11-22 10:42:10 +00:00
|
|
|
*pcalls = append(*pcalls, calls...)
|
2020-05-01 15:19:27 +00:00
|
|
|
g.r.target.assignSizesArray([]Arg{arg}, []Field{{Name: "", Type: arg.Type()}}, nil)
|
2017-11-22 10:42:10 +00:00
|
|
|
return arg
|
|
|
|
}
|
2018-01-24 18:28:36 +00:00
|
|
|
|
|
|
|
func (g *Gen) MutateArg(arg0 Arg) (calls []*Call) {
|
|
|
|
updateSizes := true
|
|
|
|
for stop := false; !stop; stop = g.r.oneOf(3) {
|
2018-02-18 13:45:32 +00:00
|
|
|
ma := &mutationArgs{target: g.r.target, ignoreSpecial: true}
|
|
|
|
ForeachSubArg(arg0, ma.collectArg)
|
|
|
|
if len(ma.args) == 0 {
|
2018-01-24 18:28:36 +00:00
|
|
|
// TODO(dvyukov): probably need to return this condition
|
|
|
|
// and updateSizes to caller so that Mutate can act accordingly.
|
|
|
|
return
|
|
|
|
}
|
2018-02-18 13:45:32 +00:00
|
|
|
idx := g.r.Intn(len(ma.args))
|
|
|
|
arg, ctx := ma.args[idx], ma.ctxes[idx]
|
2018-02-18 12:49:48 +00:00
|
|
|
newCalls, ok := g.r.target.mutateArg(g.r, g.s, arg, ctx, &updateSizes)
|
2018-01-24 18:28:36 +00:00
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
2018-02-01 14:20:37 +00:00
|
|
|
calls = append(calls, newCalls...)
|
2018-01-24 18:28:36 +00:00
|
|
|
}
|
|
|
|
return calls
|
|
|
|
}
|
2018-12-07 11:44:45 +00:00
|
|
|
|
2018-12-08 07:40:03 +00:00
|
|
|
type Builder struct {
|
2018-12-07 11:44:45 +00:00
|
|
|
target *Target
|
|
|
|
ma *memAlloc
|
|
|
|
p *Prog
|
|
|
|
}
|
|
|
|
|
2018-12-08 07:40:03 +00:00
|
|
|
func MakeProgGen(target *Target) *Builder {
|
|
|
|
return &Builder{
|
2018-12-07 11:44:45 +00:00
|
|
|
target: target,
|
|
|
|
ma: newMemAlloc(target.NumPages * target.PageSize),
|
|
|
|
p: &Prog{
|
|
|
|
Target: target,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-08 07:40:03 +00:00
|
|
|
func (pg *Builder) Append(c *Call) error {
|
2018-12-07 11:44:45 +00:00
|
|
|
pg.target.assignSizesCall(c)
|
2020-03-14 15:42:00 +00:00
|
|
|
pg.target.sanitize(c, true)
|
2018-12-07 11:44:45 +00:00
|
|
|
pg.p.Calls = append(pg.p.Calls, c)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-12-08 07:40:03 +00:00
|
|
|
func (pg *Builder) Allocate(size uint64) uint64 {
|
2018-12-07 11:44:45 +00:00
|
|
|
return pg.ma.alloc(nil, size)
|
|
|
|
}
|
|
|
|
|
2018-12-08 07:40:03 +00:00
|
|
|
func (pg *Builder) AllocateVMA(npages uint64) uint64 {
|
2018-12-07 11:48:59 +00:00
|
|
|
psize := pg.target.PageSize
|
|
|
|
addr := pg.ma.alloc(nil, (npages+1)*psize)
|
|
|
|
return (addr + psize - 1) & ^(psize - 1)
|
|
|
|
}
|
|
|
|
|
2018-12-08 07:40:03 +00:00
|
|
|
func (pg *Builder) Finalize() (*Prog, error) {
|
2018-12-07 11:44:45 +00:00
|
|
|
if err := pg.p.validate(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if _, err := pg.p.SerializeForExec(make([]byte, ExecBufferSize)); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
p := pg.p
|
|
|
|
pg.p = nil
|
|
|
|
return p, nil
|
|
|
|
}
|