aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/syscall_descriptions_syntax.md4
-rw-r--r--pkg/compiler/testdata/all.txt3
-rw-r--r--pkg/compiler/testdata/errors.txt7
-rw-r--r--pkg/compiler/types.go24
-rw-r--r--pkg/host/machine_info.go8
-rw-r--r--pkg/host/machine_info_linux.go14
-rw-r--r--prog/encoding.go2
-rw-r--r--prog/hints.go2
-rw-r--r--prog/mutation.go6
-rw-r--r--prog/prio.go2
-rw-r--r--prog/rand.go2
-rw-r--r--prog/target.go25
-rw-r--r--prog/types.go1
-rw-r--r--sys/linux/sys.txt2
-rw-r--r--syz-fuzzer/fuzzer.go32
15 files changed, 120 insertions, 14 deletions
diff --git a/docs/syscall_descriptions_syntax.md b/docs/syscall_descriptions_syntax.md
index 76cdc90b1..4517963eb 100644
--- a/docs/syscall_descriptions_syntax.md
+++ b/docs/syscall_descriptions_syntax.md
@@ -9,7 +9,7 @@ arg = argname type
argname = identifier
type = typename [ "[" type-options "]" ]
typename = "const" | "intN" | "intptr" | "flags" | "array" | "ptr" |
- "string" | "strconst" | "filename" | "len" |
+ "string" | "strconst" | "filename" | "glob" | "len" |
"bytesize" | "bytesizeN" | "bitsize" | "vma" | "proc"
type-options = [type-opt ["," type-opt]]
```
@@ -42,6 +42,8 @@ rest of the type-options are type-specific:
"stringnoz": a non-zero-terminated memory buffer (no pointer indirection implied), type-options:
either a string value in quotes for constant strings (e.g. "foo" or `deadbeef` for hex literal),
or a reference to string flags,
+"glob": glob pattern to match on target files, type-options:
+ a zero-terminated glob pattern string in quotes (e.g. "/sys/" or `/sys/**/*` for glob match),
"fmt": a string representation of an integer (not zero-terminated), type-options:
format (one of "dec", "hex", "oct") and the value (a resource, int, flags, const or proc)
the resulting data is always fixed-size (formatted as "%020llu", "0x%016llx" or "%023llo", respectively)
diff --git a/pkg/compiler/testdata/all.txt b/pkg/compiler/testdata/all.txt
index 17b7c838d..b1adf2321 100644
--- a/pkg/compiler/testdata/all.txt
+++ b/pkg/compiler/testdata/all.txt
@@ -252,6 +252,9 @@ foo_templ9(a ptr[in, templ_base3[int64]])
foo_templ10(a ptr[in, templ_base4[int8]])
foo_templ11(a ptr[in, templ_base5[42, int8]])
+foo_glob0(a ptr[in, glob["/sys/"]])
+foo_glob1(a ptr[in, glob["/sys/**/*"]])
+
# Structs.
s0 {
diff --git a/pkg/compiler/testdata/errors.txt b/pkg/compiler/testdata/errors.txt
index d3658d239..670cc0193 100644
--- a/pkg/compiler/testdata/errors.txt
+++ b/pkg/compiler/testdata/errors.txt
@@ -334,6 +334,11 @@ foo$210(a ptr[in, templ11[0, 1, int8]]) ### template templ11 needs 2 arguments
foo$211(a ptr[in, templ9]) ### template templ9 needs 1 arguments instead of 0
foo$212(a ptr[in, templ11[1]]) ### template templ11 needs 2 arguments instead of 1
+foo$glob001(a ptr[in, glob[1]]) ### unexpected int 1, string arg must be a string literal or string flags
+foo$glob002(a ptr[in, glob]) ### glob only accepts 1 arg, provided 0
+foo$glob003(a ptr[in, glob["/sys", 5]]) ### glob only accepts 1 arg, provided 2
+foo$glob004(a ptr[in, glob["/sys", 5, 2]]) ### wrong number of arguments for type glob, expect [literal or flags], [size], [opt]
+
# fmt
foo$fmt0(a ptr[in, fmt]) ### wrong number of arguments for type fmt, expect format, value
@@ -355,4 +360,4 @@ struct$perfielddir {
f4 int32 (in, inout) ### arg/field has multiple direction attributes
f5 int32 (out, inout) ### arg/field has multiple direction attributes
f6 int32 (in, out, inout) ### arg/field has multiple direction attributes
-} \ No newline at end of file
+}
diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go
index 038a05f24..d62c8fd48 100644
--- a/pkg/compiler/types.go
+++ b/pkg/compiler/types.go
@@ -598,10 +598,11 @@ func genTextType(t *ast.Type) prog.TextKind {
const (
stringnoz = "stringnoz"
+ glob = "glob"
)
var typeString = &typeDesc{
- Names: []string{"string", stringnoz},
+ Names: []string{"string", glob, stringnoz},
CanBeTypedef: true,
OptArgs: 2,
Args: []namedArg{
@@ -612,6 +613,15 @@ var typeString = &typeDesc{
if t.Ident == stringnoz && len(args) > 1 {
comp.error(args[0].Pos, "fixed-size string can't be non-zero-terminated")
}
+ if t.Ident == glob {
+ pos := t.Pos
+ if len(args) > 0 {
+ pos = args[0].Pos
+ }
+ if len(args) != 1 {
+ comp.error(pos, "glob only accepts 1 arg, provided %v", len(args))
+ }
+ }
},
CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) {
if len(args) > 1 {
@@ -626,6 +636,9 @@ var typeString = &typeDesc{
}
},
Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type) bool {
+ if t.Ident == glob {
+ return true
+ }
return comp.stringSize(t, args) == varlenString
},
ZeroSize: func(comp *compiler, t *ast.Type, args []*ast.Type) bool {
@@ -645,6 +658,15 @@ var typeString = &typeDesc{
NoZ: t.Ident == stringnoz,
}
}
+ if len(args) > 0 && t.Ident == glob {
+ base.TypeSize = 0
+ return &prog.BufferType{
+ TypeCommon: base.TypeCommon,
+ Kind: prog.BufferGlob,
+ SubKind: args[0].String,
+ NoZ: false,
+ }
+ }
subkind := ""
if len(args) > 0 && args[0].Ident != "" {
subkind = args[0].Ident
diff --git a/pkg/host/machine_info.go b/pkg/host/machine_info.go
index a804ca32a..167b7ec03 100644
--- a/pkg/host/machine_info.go
+++ b/pkg/host/machine_info.go
@@ -38,8 +38,16 @@ func CollectModulesInfo() ([]KernelModule, error) {
return machineModulesInfo()
}
+func CollectGlobsInfo(globs []string) (map[string][]string, error) {
+ if machineModulesInfo == nil {
+ return nil, nil
+ }
+ return machineGlobsInfo(globs)
+}
+
var machineInfoFuncs []machineInfoFunc
var machineModulesInfo func() ([]KernelModule, error)
+var machineGlobsInfo func([]string) (map[string][]string, error)
type machineInfoFunc struct {
name string
diff --git a/pkg/host/machine_info_linux.go b/pkg/host/machine_info_linux.go
index 317d34cd9..3b0971495 100644
--- a/pkg/host/machine_info_linux.go
+++ b/pkg/host/machine_info_linux.go
@@ -21,6 +21,7 @@ func init() {
{"KVM", readKVMInfo},
}
machineModulesInfo = getModulesInfo
+ machineGlobsInfo = getGlobsInfo
}
func readCPUInfo(buffer *bytes.Buffer) error {
@@ -140,3 +141,16 @@ func getModulesInfo() ([]KernelModule, error) {
}
return modules, nil
}
+
+func getGlobsInfo(globs []string) (map[string][]string, error) {
+ files := make(map[string][]string, len(globs))
+ for _, glob := range globs {
+ matches, err := filepath.Glob(glob)
+ if err != nil {
+ return nil, err
+ }
+ files[glob] = matches
+ }
+
+ return files, nil
+}
diff --git a/prog/encoding.go b/prog/encoding.go
index 140636865..e66535e2a 100644
--- a/prog/encoding.go
+++ b/prog/encoding.go
@@ -853,7 +853,7 @@ func encodeData(buf *bytes.Buffer, data []byte, readable, cstr bool) {
}
func isReadableDataType(typ *BufferType) bool {
- return typ.Kind == BufferString || typ.Kind == BufferFilename
+ return typ.Kind == BufferString || typ.Kind == BufferFilename || typ.Kind == BufferGlob
}
func isReadableData(data []byte) bool {
diff --git a/prog/hints.go b/prog/hints.go
index a26185010..c66242357 100644
--- a/prog/hints.go
+++ b/prog/hints.go
@@ -102,7 +102,7 @@ func generateHints(compMap CompMap, arg Arg, exec func()) {
case BufferFilename:
// This can generate escaping paths and is probably not too useful anyway.
return
- case BufferString:
+ case BufferString, BufferGlob:
if len(t.Values) != 0 {
// These are frequently file names or complete enumerations.
// Mutating these may be useful iff we intercept strcmp
diff --git a/prog/mutation.go b/prog/mutation.go
index af6a458f0..9d6dc2118 100644
--- a/prog/mutation.go
+++ b/prog/mutation.go
@@ -349,6 +349,12 @@ func (t *BufferType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []
}
case BufferFilename:
a.data = []byte(r.filename(s, t))
+ case BufferGlob:
+ if len(t.Values) != 0 {
+ a.data = r.randString(s, t)
+ } else {
+ a.data = []byte(r.filename(s, t))
+ }
case BufferText:
data := append([]byte{}, a.Data()...)
a.data = r.mutateText(t.Text, data)
diff --git a/prog/prio.go b/prog/prio.go
index 2c3436151..4cf3daf90 100644
--- a/prog/prio.go
+++ b/prog/prio.go
@@ -97,7 +97,7 @@ func (target *Target) calcResourceUsage() map[string]map[int]weights {
case *BufferType:
switch a.Kind {
case BufferBlobRand, BufferBlobRange, BufferText:
- case BufferString:
+ case BufferString, BufferGlob:
if a.SubKind != "" {
noteUsage(uses, c, 2, ctx.Dir, fmt.Sprintf("str-%v", a.SubKind))
}
diff --git a/prog/rand.go b/prog/rand.go
index a78f7e343..7ab5068cb 100644
--- a/prog/rand.go
+++ b/prog/rand.go
@@ -734,6 +734,8 @@ func (a *BufferType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*
return MakeOutDataArg(a, dir, sz), nil
}
return MakeDataArg(a, dir, []byte(r.filename(s, a))), nil
+ case BufferGlob:
+ return MakeDataArg(a, dir, r.randString(s, a)), nil
case BufferText:
if dir == DirOut {
return MakeOutDataArg(a, dir, uint64(r.Intn(100))), nil
diff --git a/prog/target.go b/prog/target.go
index f2eaf6ead..974880c22 100644
--- a/prog/target.go
+++ b/prog/target.go
@@ -227,6 +227,31 @@ func (target *Target) DefaultChoiceTable() *ChoiceTable {
return target.defaultChoiceTable
}
+func (target *Target) GetGlobs() []string {
+ var globs []string
+ ForeachType(target.Syscalls, func(typ Type, ctx TypeCtx) {
+ switch a := typ.(type) {
+ case *BufferType:
+ if a.Kind == BufferGlob && a.SubKind != "" {
+ globs = append(globs, a.SubKind)
+ }
+ }
+ })
+
+ return globs
+}
+
+func (target *Target) UpdateGlobFilesForType(globFiles map[string][]string) {
+ ForeachType(target.Syscalls, func(typ Type, ctx TypeCtx) {
+ switch a := typ.(type) {
+ case *BufferType:
+ if a.Kind == BufferGlob && a.SubKind != "" {
+ a.Values = globFiles[a.SubKind]
+ }
+ }
+ })
+}
+
type Gen struct {
r *randGen
s *state
diff --git a/prog/types.go b/prog/types.go
index fb680f0a1..66ce3ced0 100644
--- a/prog/types.go
+++ b/prog/types.go
@@ -481,6 +481,7 @@ const (
BufferString
BufferFilename
BufferText
+ BufferGlob
)
type TextKind int
diff --git a/sys/linux/sys.txt b/sys/linux/sys.txt
index c2725bfe7..ab39e6ded 100644
--- a/sys/linux/sys.txt
+++ b/sys/linux/sys.txt
@@ -78,6 +78,8 @@ type signalnoptr intptr[0:65]
syz_execute_func(text ptr[in, text[target]])
+openat$sysfs(fd const[AT_FDCWD], dir ptr[in, glob["/sys/**/*"]], flags flags[open_flags], mode flags[open_mode]) fd
+
open(file ptr[in, filename], flags flags[open_flags], mode flags[open_mode]) fd
# Just so that we have something that creates fd_dir resources.
open$dir(file ptr[in, filename], flags flags[open_flags], mode flags[open_mode]) fd_dir
diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go
index 4e8c1bb93..9fccaf5c8 100644
--- a/syz-fuzzer/fuzzer.go
+++ b/syz-fuzzer/fuzzer.go
@@ -179,14 +179,7 @@ func main() {
return
}
- machineInfo, err := host.CollectMachineInfo()
- if err != nil {
- log.Fatalf("failed to collect machine information: %v", err)
- }
- modules, err := host.CollectModulesInfo()
- if err != nil {
- log.Fatalf("failed to collect modules info: %v", err)
- }
+ machineInfo, modules := collectMachineInfos(target)
log.Logf(0, "dialing manager at %v", *flagManager)
manager, err := rpctype.NewRPCClient(*flagManager, timeouts.Scale)
@@ -298,6 +291,29 @@ func main() {
fuzzer.pollLoop()
}
+func collectMachineInfos(target *prog.Target) ([]byte, []host.KernelModule) {
+ machineInfo, err := host.CollectMachineInfo()
+ if err != nil {
+ log.Fatalf("failed to collect machine information: %v", err)
+ }
+ modules, err := host.CollectModulesInfo()
+ if err != nil {
+ log.Fatalf("failed to collect modules info: %v", err)
+ }
+
+ var globFiles map[string][]string
+ globs := target.GetGlobs()
+ if len(globs) != 0 {
+ globFiles, err = host.CollectGlobsInfo(globs)
+ if err != nil {
+ log.Fatalf("faield to collect globFiles info: %v", err)
+ }
+ target.UpdateGlobFilesForType(globFiles)
+ }
+
+ return machineInfo, modules
+}
+
// Returns gateCallback for leak checking if enabled.
func (fuzzer *Fuzzer) useBugFrames(r *rpctype.ConnectRes, flagProcs int) func() {
var gateCallback func()