sys: add proc type to denote per proccess integers

This commit is contained in:
Andrey Konovalov 2016-11-25 15:53:10 +01:00
parent 16491e22d5
commit 253a40f30d
19 changed files with 106 additions and 37 deletions

View File

@ -142,6 +142,9 @@ func parse(data []byte) (*Config, map[int]bool, []*regexp.Regexp, error) {
if cfg.Procs <= 0 {
cfg.Procs = 1
}
if cfg.Procs > 32 {
return nil, nil, nil, fmt.Errorf("config param procs has higher value '%v' then the max supported 32", cfg.Procs)
}
if cfg.Output == "" {
if cfg.Type == "local" {
cfg.Output = "none"

View File

@ -29,7 +29,7 @@ type Options struct {
}
func Write(p *prog.Prog, opts Options) ([]byte, error) {
exec := p.SerializeForExec()
exec := p.SerializeForExec(0)
w := new(bytes.Buffer)
fmt.Fprint(w, "// autogenerated by syzkaller (http://github.com/google/syzkaller)\n\n")

View File

@ -31,6 +31,7 @@ type Env struct {
bin []string
timeout time.Duration
flags uint64
pid int
StatExecs uint64
StatRestarts uint64
@ -85,7 +86,7 @@ func DefaultFlags() (uint64, time.Duration, error) {
return flags, *flagTimeout, nil
}
func MakeEnv(bin string, timeout time.Duration, flags uint64) (*Env, error) {
func MakeEnv(bin string, timeout time.Duration, flags uint64, pid int) (*Env, error) {
// IPC timeout must be larger then executor timeout.
// Otherwise IPC will kill parent executor but leave child executor alive.
if timeout < 7*time.Second {
@ -121,6 +122,7 @@ func MakeEnv(bin string, timeout time.Duration, flags uint64) (*Env, error) {
bin: strings.Split(bin, " "),
timeout: timeout,
flags: flags,
pid: pid,
}
if len(env.bin) == 0 {
return nil, fmt.Errorf("binary is empty string")
@ -159,7 +161,7 @@ func (env *Env) Close() error {
func (env *Env) Exec(p *prog.Prog) (output []byte, cov [][]uint32, errnos []int, failed, hanged bool, err0 error) {
if p != nil {
// Copy-in serialized program.
progData := p.SerializeForExec()
progData := p.SerializeForExec(env.pid)
if len(progData) > len(env.In) {
panic("program is too long")
}

View File

@ -52,7 +52,7 @@ func TestEmptyProg(t *testing.T) {
bin := buildExecutor(t)
defer os.Remove(bin)
env, err := MakeEnv(bin, timeout, 0)
env, err := MakeEnv(bin, timeout, 0, 0)
if err != nil {
t.Fatalf("failed to create env: %v", err)
}
@ -82,7 +82,7 @@ func TestExecute(t *testing.T) {
flags := []uint64{0, FlagThreaded, FlagThreaded | FlagCollide}
for _, flag := range flags {
t.Logf("testing flags 0x%x\n", flag)
env, err := MakeEnv(bin, timeout, flag)
env, err := MakeEnv(bin, timeout, flag, 0)
if err != nil {
t.Fatalf("failed to create env: %v", err)
}

View File

@ -30,7 +30,7 @@ const (
dataOffset = 512 << 20
)
func (p *Prog) SerializeForExec() []byte {
func (p *Prog) SerializeForExec(pid int) []byte {
if err := p.validate(); err != nil {
panic(fmt.Errorf("serializing invalid program: %v", err))
}
@ -72,7 +72,7 @@ func (p *Prog) SerializeForExec() []byte {
if arg1.Type.Dir() != sys.DirOut {
w.write(ExecInstrCopyin)
w.write(physicalAddr(arg) + w.args[arg1].Offset)
w.writeArg(arg1)
w.writeArg(arg1, pid)
instrSeq++
}
}
@ -83,7 +83,7 @@ func (p *Prog) SerializeForExec() []byte {
w.write(uintptr(c.Meta.ID))
w.write(uintptr(len(c.Args)))
for _, arg := range c.Args {
w.writeArg(arg)
w.writeArg(arg, pid)
}
w.args[c.Ret] = &argInfo{Idx: instrSeq}
instrSeq++
@ -143,12 +143,12 @@ func (w *execContext) write(v uintptr) {
w.buf = append(w.buf, byte(v>>0), byte(v>>8), byte(v>>16), byte(v>>24), byte(v>>32), byte(v>>40), byte(v>>48), byte(v>>56))
}
func (w *execContext) writeArg(arg *Arg) {
func (w *execContext) writeArg(arg *Arg, pid int) {
switch arg.Kind {
case ArgConst:
w.write(ExecArgConst)
w.write(arg.Size())
w.write(arg.Value())
w.write(arg.Value(pid))
case ArgResult:
w.write(ExecArgResult)
w.write(arg.Size())

View File

@ -16,7 +16,7 @@ func TestSerializeForExecRandom(t *testing.T) {
rs, iters := initTest(t)
for i := 0; i < iters; i++ {
p := Generate(rs, 10, nil)
p.SerializeForExec()
p.SerializeForExec(i % 16)
}
}
@ -159,7 +159,7 @@ func TestSerializeForExec(t *testing.T) {
t.Fatalf("failed to deserialize prog %v: %v", i, err)
}
t.Run(fmt.Sprintf("%v:%v", i, p.String()), func(t *testing.T) {
data := p.SerializeForExec()
data := p.SerializeForExec(i % 16)
w := new(bytes.Buffer)
binary.Write(w, binary.LittleEndian, test.serialized)
if !bytes.Equal(data, w.Bytes()) {

View File

@ -69,7 +69,7 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable, corpus []*Pro
baseSize = base.Res.Size()
}
switch a := arg.Type.(type) {
case *sys.IntType, *sys.FlagsType, *sys.ResourceType, *sys.VmaType:
case *sys.IntType, *sys.FlagsType, *sys.ResourceType, *sys.VmaType, *sys.ProcType:
arg1, calls1 := r.generateArg(s, arg.Type)
p.replaceArg(c, arg, arg1, calls1)
case *sys.BufferType:
@ -326,7 +326,7 @@ func Minimize(p0 *Prog, callIndex0 int, pred func(*Prog, int) bool, crash bool)
return true
}
}
case *sys.IntType, *sys.FlagsType, *sys.ResourceType:
case *sys.IntType, *sys.FlagsType, *sys.ResourceType, *sys.ProcType:
// TODO: try to reset bits in ints
// TODO: try to set separate flags
if crash {

View File

@ -101,8 +101,6 @@ func calcStaticPriorities() [][]float32 {
noteUsage(1.0, "signalno")
case sys.IntInaddr:
noteUsage(1.0, "inaddr")
case sys.IntInport:
noteUsage(1.0, "inport")
default:
panic("unknown int kind")
}

View File

@ -85,7 +85,7 @@ func encodeValue(value, size uintptr, bigEndian bool) uintptr {
}
// Returns value taking endianness into consideration.
func (a *Arg) Value() uintptr {
func (a *Arg) Value(pid int) uintptr {
switch typ := a.Type.(type) {
case *sys.IntType:
return encodeValue(a.Val, typ.Size(), typ.BigEndian)
@ -95,6 +95,9 @@ func (a *Arg) Value() uintptr {
return encodeValue(a.Val, typ.Size(), typ.BigEndian)
case *sys.LenType:
return encodeValue(a.Val, typ.Size(), typ.BigEndian)
case *sys.ProcType:
val := uintptr(typ.ValuesStart) + uintptr(typ.ValuesPerProc) * uintptr(pid) + a.Val
return encodeValue(val, typ.Size(), typ.BigEndian)
}
return a.Val
}
@ -102,7 +105,7 @@ func (a *Arg) Value() uintptr {
func (a *Arg) Size() uintptr {
switch typ := a.Type.(type) {
case *sys.IntType, *sys.LenType, *sys.FlagsType, *sys.ConstType,
*sys.ResourceType, *sys.VmaType, *sys.PtrType:
*sys.ResourceType, *sys.VmaType, *sys.PtrType, *sys.ProcType:
return typ.Size()
case *sys.BufferType:
return uintptr(len(a.Data))

View File

@ -634,7 +634,7 @@ func (r *randGen) generateArg(s *state, typ sys.Type) (arg *Arg, calls []*Call)
// output arguments (their elements can be referenced in subsequent calls).
switch typ.(type) {
case *sys.IntType, *sys.FlagsType, *sys.ConstType,
*sys.ResourceType, *sys.VmaType:
*sys.ResourceType, *sys.VmaType, *sys.ProcType:
return constArg(typ, typ.Default()), nil
}
}
@ -720,8 +720,6 @@ func (r *randGen) generateArg(s *state, typ sys.Type) (arg *Arg, calls []*Call)
v %= 130
case sys.IntInaddr:
v = uintptr(r.inaddr(s))
case sys.IntInport:
v = uintptr(r.inport(s))
case sys.IntFileoff:
r.choose(
90, func() { v = 0 },
@ -732,6 +730,8 @@ func (r *randGen) generateArg(s *state, typ sys.Type) (arg *Arg, calls []*Call)
v = r.randRangeInt(a.RangeBegin, a.RangeEnd)
}
return constArg(a, v), nil
case *sys.ProcType:
return constArg(a, r.rand(int(a.ValuesPerProc))), nil
case *sys.ArrayType:
count := uintptr(0)
switch a.Kind {

View File

@ -69,7 +69,7 @@ func (c *Call) validate(ctx *validCtx) error {
}
}
}
switch arg.Type.(type) {
switch typ1 := arg.Type.(type) {
case *sys.ResourceType:
switch arg.Kind {
case ArgResult:
@ -93,6 +93,10 @@ func (c *Call) validate(ctx *validCtx) error {
default:
return fmt.Errorf("syscall %v: union arg '%v' has bad kind %v", c.Meta.Name, typ.Name(), arg.Kind)
}
case *sys.ProcType:
if arg.Val >= uintptr(typ1.ValuesPerProc) {
return fmt.Errorf("syscall %v: per proc arg '%v' has bad value '%v'", c.Meta.Name, typ.Name(), arg.Val)
}
}
switch arg.Kind {
case ArgConst:

View File

@ -23,7 +23,7 @@ Pseudo-formal grammar of syscall description:
type = typename [ "[" type-options "]" ]
typename = "const" | "intN" | "intptr" | "flags" | "array" | "ptr" |
"buffer" | "string" | "strconst" | "filename" |
"len" | "bytesize" | "vma"
"len" | "bytesize" | "vma" | "proc"
type-options = [type-opt ["," type-opt]]
```
common type-options include:
@ -55,6 +55,8 @@ rest of the type-options are type-specific:
"bytesize": similar to "len", but always denotes the size in bytes, type-options:
argname of the object
"vma": a pointer to a set of pages (used as input for mmap/munmap/mremap/madvise)
"proc": per process int (see description below), type-options:
underlying type, value range start, how many values per process
```
flags/len/flags also have trailing underlying type type-option when used in structs/unions/pointers.
@ -108,6 +110,15 @@ accept(fd sock, ...) sock
listen(fd sock, backlog int32)
```
### Proc
The `proc` type can be used to denote per process integers.
The idea is to have a separate range of values for each executor, so they don't interfere.
The simplest example is a port number.
The `proc[int16be, 20000, 4]` type means that we want to generate an `int16be` integer starting from `20000` and assign no more than `4` integers for each process.
As a result the executor number `n` will get values in the `[20000 + n * 4, 20000 + (n + 1) * 4)` range.
### Misc
Description files also contain `include` directives that refer to Linux kernel header files

View File

@ -207,7 +207,6 @@ const (
IntPlain IntKind = iota
IntSignalno
IntInaddr
IntInport
IntFileoff // offset within a file
IntRange
)
@ -229,6 +228,22 @@ func (t *IntType) Align() uintptr {
return t.Size()
}
type ProcType struct {
TypeCommon
TypeSize uintptr
BigEndian bool
ValuesStart int64
ValuesPerProc uint64
}
func (t *ProcType) Size() uintptr {
return t.TypeSize
}
func (t *ProcType) Align() uintptr {
return t.Size()
}
type ArrayKind int
const (
@ -478,7 +493,7 @@ func ForeachType(meta *Call, f func(Type)) {
rec(opt)
}
case *ResourceType, *BufferType, *VmaType, *LenType,
*FlagsType, *ConstType, *IntType:
*FlagsType, *ConstType, *IntType, *ProcType:
default:
panic("unknown type")
}

View File

@ -119,7 +119,7 @@ sockopt_opt_ipv6_mreq = IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_JOIN_ANY
sockaddr_in {
family const[AF_INET, int16]
port in_port
port proc[int16be, 20000, 4]
addr in_addr
}
@ -130,7 +130,7 @@ sockaddr_storage_in {
sockaddr_in6 {
family const[AF_INET6, int16]
port in_port
port proc[int16be, 20000, 4]
flow int32
addr in6_addr
scope int32

View File

@ -975,9 +975,9 @@ xfrm_user_tmpl {
xfrm_selector {
daddr in_addr_any
saddr in_addr_any
dport in_port
dport proc[int16be, 20000, 4]
dmask int16
sport in_port
sport proc[int16be, 20000, 4]
smask int16
fam int16
len_d int8

View File

@ -532,6 +532,44 @@ func generateArg(
skipSyscall(fmt.Sprintf("missing const %v", a[0]))
}
fmt.Fprintf(out, "&ConstType{%v, TypeSize: %v, BigEndian: %v, Val: uintptr(%v)}", common(), size, bigEndian, val)
case "proc":
canBeArg = true
size := uint64(ptrSize)
bigEndian := false
var valuesStart string
var valuesPerProc string
if isField {
if want := 3; len(a) != want {
failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
}
size, bigEndian = decodeIntType(a[0])
valuesStart = a[1]
valuesPerProc = a[2]
} else {
if want := 2; len(a) != want {
failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
}
valuesStart = a[0]
valuesPerProc = a[1]
}
valuesStartInt, err := strconv.ParseInt(valuesStart, 10, 64)
if err != nil {
failf("couldn't parse '%v' as int64", valuesStart)
}
valuesPerProcInt, err := strconv.ParseInt(valuesPerProc, 10, 64)
if err != nil {
failf("couldn't parse '%v' as int64", valuesPerProc)
}
if valuesPerProcInt < 1 {
failf("values per proc '%v' should be >= 1", valuesPerProcInt)
}
if valuesStartInt >= (1 << (size * 8)) {
failf("values starting from '%v' overflow desired type of size '%v'", valuesStartInt, size)
}
if valuesStartInt + 32 * valuesPerProcInt >= (1 << (size * 8)) {
failf("not enough values starting from '%v' with step '%v' and type size '%v' for 32 procs", valuesStartInt, valuesPerProcInt, size)
}
fmt.Fprintf(out, "&ProcType{%v, TypeSize: %v, BigEndian: %v, ValuesStart: %v, ValuesPerProc: %v}", common(), size, bigEndian, valuesStartInt, valuesPerProcInt)
case "int8", "int16", "int32", "int64", "intptr", "int16be", "int32be", "int64be", "intptrbe":
canBeArg = true
size, bigEndian := decodeIntType(typ)
@ -555,11 +593,6 @@ func generateArg(
failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
}
fmt.Fprintf(out, "&IntType{%v, TypeSize: 4, Kind: IntInaddr}", common())
case "in_port":
if want := 0; len(a) != want {
failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
}
fmt.Fprintf(out, "&IntType{%v, TypeSize: 2, Kind: IntInport}", common())
case "filename":
canBeArg = true
if want := 0; len(a) != want {

View File

@ -151,7 +151,7 @@ func main() {
needPoll <- struct{}{}
envs := make([]*ipc.Env, *flagProcs)
for pid := 0; pid < *flagProcs; pid++ {
env, err := ipc.MakeEnv(*flagExecutor, timeout, flags)
env, err := ipc.MakeEnv(*flagExecutor, timeout, flags, pid)
if err != nil {
panic(err)
}

View File

@ -76,7 +76,7 @@ func main() {
pid := p
go func() {
defer wg.Done()
env, err := ipc.MakeEnv(*flagExecutor, timeout, flags)
env, err := ipc.MakeEnv(*flagExecutor, timeout, flags, pid)
if err != nil {
Fatalf("failed to create ipc env: %v", err)
}

View File

@ -55,7 +55,7 @@ func main() {
for pid := 0; pid < *flagProcs; pid++ {
pid := pid
go func() {
env, err := ipc.MakeEnv(*flagExecutor, timeout, flags)
env, err := ipc.MakeEnv(*flagExecutor, timeout, flags, pid)
if err != nil {
Fatalf("failed to create execution environment: %v", err)
}