aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey Konovalov <andreyknvl@google.com>2016-11-25 15:53:10 +0100
committerAndrey Konovalov <andreyknvl@google.com>2016-11-25 17:51:41 +0100
commit253a40f30dd55de31e4951a9bb02d02a7c6ba020 (patch)
tree8c4ec6a874c93e307cb5d0e4c5482f83428021ea
parent16491e22d5dd32609c5bfc7cc4e15d463ff47e52 (diff)
sys: add proc type to denote per proccess integers
-rw-r--r--config/config.go3
-rw-r--r--csource/csource.go2
-rw-r--r--ipc/ipc.go6
-rw-r--r--ipc/ipc_test.go4
-rw-r--r--prog/encodingexec.go10
-rw-r--r--prog/encodingexec_test.go4
-rw-r--r--prog/mutation.go4
-rw-r--r--prog/prio.go2
-rw-r--r--prog/prog.go7
-rw-r--r--prog/rand.go6
-rw-r--r--prog/validation.go6
-rw-r--r--sys/README.md13
-rw-r--r--sys/decl.go19
-rw-r--r--sys/socket.txt4
-rw-r--r--sys/sys.txt4
-rw-r--r--sysgen/sysgen.go43
-rw-r--r--syz-fuzzer/fuzzer.go2
-rw-r--r--tools/syz-execprog/execprog.go2
-rw-r--r--tools/syz-stress/stress.go2
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)
}