From 253a40f30dd55de31e4951a9bb02d02a7c6ba020 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Fri, 25 Nov 2016 15:53:10 +0100 Subject: sys: add proc type to denote per proccess integers --- config/config.go | 3 +++ csource/csource.go | 2 +- ipc/ipc.go | 6 ++++-- ipc/ipc_test.go | 4 ++-- prog/encodingexec.go | 10 +++++----- prog/encodingexec_test.go | 4 ++-- prog/mutation.go | 4 ++-- prog/prio.go | 2 -- prog/prog.go | 7 +++++-- prog/rand.go | 6 +++--- prog/validation.go | 6 +++++- sys/README.md | 13 ++++++++++++- sys/decl.go | 19 +++++++++++++++++-- sys/socket.txt | 4 ++-- sys/sys.txt | 4 ++-- sysgen/sysgen.go | 43 +++++++++++++++++++++++++++++++++++++----- syz-fuzzer/fuzzer.go | 2 +- tools/syz-execprog/execprog.go | 2 +- tools/syz-stress/stress.go | 2 +- 19 files changed, 106 insertions(+), 37 deletions(-) diff --git a/config/config.go b/config/config.go index 103238f0a..33e9c625e 100644 --- a/config/config.go +++ b/config/config.go @@ -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" diff --git a/csource/csource.go b/csource/csource.go index 49c15fe72..4464deb5c 100644 --- a/csource/csource.go +++ b/csource/csource.go @@ -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") diff --git a/ipc/ipc.go b/ipc/ipc.go index b48513eb4..e1cf7ebd4 100644 --- a/ipc/ipc.go +++ b/ipc/ipc.go @@ -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") } diff --git a/ipc/ipc_test.go b/ipc/ipc_test.go index 387fa2b17..042ffb902 100644 --- a/ipc/ipc_test.go +++ b/ipc/ipc_test.go @@ -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) } diff --git a/prog/encodingexec.go b/prog/encodingexec.go index 4c2cd2524..17cb2b553 100644 --- a/prog/encodingexec.go +++ b/prog/encodingexec.go @@ -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()) diff --git a/prog/encodingexec_test.go b/prog/encodingexec_test.go index 207543705..d16a6f1e2 100644 --- a/prog/encodingexec_test.go +++ b/prog/encodingexec_test.go @@ -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()) { diff --git a/prog/mutation.go b/prog/mutation.go index 9ea13d466..b03fccb5f 100644 --- a/prog/mutation.go +++ b/prog/mutation.go @@ -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 { diff --git a/prog/prio.go b/prog/prio.go index 59a205a5d..f503257ad 100644 --- a/prog/prio.go +++ b/prog/prio.go @@ -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") } diff --git a/prog/prog.go b/prog/prog.go index 7e2cfca31..fee54e37c 100644 --- a/prog/prog.go +++ b/prog/prog.go @@ -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)) diff --git a/prog/rand.go b/prog/rand.go index 99722f8d5..3d25711e5 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -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 { diff --git a/prog/validation.go b/prog/validation.go index 0bc01b91d..b2d539282 100644 --- a/prog/validation.go +++ b/prog/validation.go @@ -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: diff --git a/sys/README.md b/sys/README.md index 8125a7b7c..259a1c1f5 100644 --- a/sys/README.md +++ b/sys/README.md @@ -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 diff --git a/sys/decl.go b/sys/decl.go index 84c2f8c08..0533c3770 100644 --- a/sys/decl.go +++ b/sys/decl.go @@ -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") } diff --git a/sys/socket.txt b/sys/socket.txt index 74728194f..f33627855 100644 --- a/sys/socket.txt +++ b/sys/socket.txt @@ -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 diff --git a/sys/sys.txt b/sys/sys.txt index 613290d10..c45eb6630 100644 --- a/sys/sys.txt +++ b/sys/sys.txt @@ -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 diff --git a/sysgen/sysgen.go b/sysgen/sysgen.go index fe68ed15b..27f75b1ea 100644 --- a/sysgen/sysgen.go +++ b/sysgen/sysgen.go @@ -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 { diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index d795a28c8..42e2768e1 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -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) } diff --git a/tools/syz-execprog/execprog.go b/tools/syz-execprog/execprog.go index 48a686e7f..dba98f4ec 100644 --- a/tools/syz-execprog/execprog.go +++ b/tools/syz-execprog/execprog.go @@ -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) } diff --git a/tools/syz-stress/stress.go b/tools/syz-stress/stress.go index a357bda99..ebf6ec9b4 100644 --- a/tools/syz-stress/stress.go +++ b/tools/syz-stress/stress.go @@ -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) } -- cgit mrf-deployment