aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2022-08-09 12:40:58 +0200
committerDmitry Vyukov <dvyukov@google.com>2022-08-10 11:45:49 +0200
commit8fad22bf840fc968171bee6cf294f026eb1e0d3a (patch)
tree2bad644606904f4e75ef6fe689bac49e91877c40
parenta12254451cfa5e5129a0c091255d502b6805adf6 (diff)
prog: generate very long file names
Generate very long file names once in a while to provoke bugs like: https://github.com/google/gvisor/commit/f857f268eceb1cdee0b2bdfa218c969c84033fcd
-rw-r--r--prog/minimization.go60
-rw-r--r--prog/minimization_test.go12
-rw-r--r--prog/mutation.go9
-rw-r--r--prog/mutation_test.go10
-rw-r--r--prog/rand.go28
-rw-r--r--prog/rand_test.go6
-rw-r--r--prog/target.go12
-rw-r--r--sys/linux/init.go12
-rw-r--r--sys/linux/sys.txt6
-rw-r--r--sys/linux/sys.txt.const3
-rw-r--r--sys/test/init.go1
-rw-r--r--sys/test/test.txt2
12 files changed, 137 insertions, 24 deletions
diff --git a/prog/minimization.go b/prog/minimization.go
index 26a4dfc93..fcb681054 100644
--- a/prog/minimization.go
+++ b/prog/minimization.go
@@ -4,6 +4,7 @@
package prog
import (
+ "bytes"
"fmt"
"reflect"
)
@@ -294,30 +295,53 @@ func (typ *ResourceType) minimize(ctx *minimizeArgsCtx, arg Arg, path string) bo
}
func (typ *BufferType) minimize(ctx *minimizeArgsCtx, arg Arg, path string) bool {
- // TODO: try to set individual bytes to 0
- if typ.Kind != BufferBlobRand && typ.Kind != BufferBlobRange || arg.Dir() == DirOut {
+ if arg.Dir() == DirOut {
return false
}
a := arg.(*DataArg)
- len0 := len(a.Data())
- minLen := int(typ.RangeBegin)
- for step := len(a.Data()) - minLen; len(a.Data()) > minLen && step > 0; {
- if len(a.Data())-step >= minLen {
- a.data = a.Data()[:len(a.Data())-step]
- ctx.target.assignSizesCall(ctx.call)
- if ctx.pred(ctx.p, ctx.callIndex0) {
- continue
+ switch typ.Kind {
+ case BufferBlobRand, BufferBlobRange:
+ // TODO: try to set individual bytes to 0
+ len0 := len(a.Data())
+ minLen := int(typ.RangeBegin)
+ for step := len(a.Data()) - minLen; len(a.Data()) > minLen && step > 0; {
+ if len(a.Data())-step >= minLen {
+ a.data = a.Data()[:len(a.Data())-step]
+ ctx.target.assignSizesCall(ctx.call)
+ if ctx.pred(ctx.p, ctx.callIndex0) {
+ continue
+ }
+ a.data = a.Data()[:len(a.Data())+step]
+ ctx.target.assignSizesCall(ctx.call)
+ }
+ step /= 2
+ if ctx.crash {
+ break
}
- a.data = a.Data()[:len(a.Data())+step]
- ctx.target.assignSizesCall(ctx.call)
}
- step /= 2
- if ctx.crash {
- break
+ if len(a.Data()) != len0 {
+ *ctx.p0 = ctx.p
+ ctx.triedPaths[path] = true
+ return true
+ }
+ case BufferFilename:
+ // Try to undo target.SpecialFileLenghts mutation
+ // and reduce file name length.
+ if !typ.Varlen() {
+ return false
+ }
+ data0 := append([]byte{}, a.Data()...)
+ a.data = bytes.TrimRight(a.Data(), specialFileLenPad+"\x00")
+ if !typ.NoZ {
+ a.data = append(a.data, 0)
+ }
+ if bytes.Equal(a.data, data0) {
+ return false
+ }
+ ctx.target.assignSizesCall(ctx.call)
+ if ctx.pred(ctx.p, ctx.callIndex0) {
+ *ctx.p0 = ctx.p
}
- }
- if len(a.Data()) != len0 {
- *ctx.p0 = ctx.p
ctx.triedPaths[path] = true
return true
}
diff --git a/prog/minimization_test.go b/prog/minimization_test.go
index 697937e92..4da791560 100644
--- a/prog/minimization_test.go
+++ b/prog/minimization_test.go
@@ -10,6 +10,7 @@ import (
// nolint:gocyclo
func TestMinimize(t *testing.T) {
+ // nolint: lll
tests := []struct {
os string
arch string
@@ -216,6 +217,17 @@ func TestMinimize(t *testing.T) {
"pipe2(0x0, 0x0) (rerun: 100)\n",
-1,
},
+ // Undo target.SpecialFileLenghts mutation (reduce file name length).
+ {
+ "test", "64",
+ "mutate9(&(0x7f0000000000)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\x00')\n",
+ 0,
+ func(p *Prog, callIndex int) bool {
+ return p.Calls[0].Args[0].(*PointerArg).Res != nil
+ },
+ "mutate9(&(0x7f0000000000)='./file0\\x00')\n",
+ 0,
+ },
}
t.Parallel()
for ti, test := range tests {
diff --git a/prog/mutation.go b/prog/mutation.go
index 022ebce62..b03987671 100644
--- a/prog/mutation.go
+++ b/prog/mutation.go
@@ -333,7 +333,11 @@ func (t *BufferType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []
}
a := arg.(*DataArg)
if a.Dir() == DirOut {
- mutateBufferSize(r, a, minLen, maxLen)
+ if t.Kind == BufferFilename && r.oneOf(100) {
+ a.size = uint64(r.randFilenameLength())
+ } else {
+ mutateBufferSize(r, a, minLen, maxLen)
+ }
return
}
switch t.Kind {
@@ -370,7 +374,8 @@ func (t *BufferType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []
func mutateBufferSize(r *randGen, arg *DataArg, minLen, maxLen uint64) {
for oldSize := arg.Size(); oldSize == arg.Size(); {
arg.size += uint64(r.Intn(33)) - 16
- if arg.size < minLen {
+ // Cast to int64 to prevent underflows.
+ if int64(arg.size) < int64(minLen) {
arg.size = minLen
}
if arg.size > maxLen {
diff --git a/prog/mutation_test.go b/prog/mutation_test.go
index a14719065..5edba991c 100644
--- a/prog/mutation_test.go
+++ b/prog/mutation_test.go
@@ -86,6 +86,7 @@ func TestMutateArgument(t *testing.T) {
if testutil.RaceEnabled {
t.Skip("skipping in race mode, too slow")
}
+ // nolint: lll
tests := [][2]string{
// Mutate an integer with a higher priority than the boolean arguments.
{
@@ -122,6 +123,15 @@ func TestMutateArgument(t *testing.T) {
`mutate_union(&(0x7f0000000000)=@f1=[0x0, 0x1, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x0, 0x0])`,
`mutate_union(&(0x7f0000000000)=@f1=[0x0, 0x1, 0xff, 0x3, 0x0, 0x1, 0x2, 0x3, 0x0, 0x0])`,
},
+ // Mutate filename using target.SpecialFileLenghts.
+ {
+ `mutate9(&(0x7f0000000000)='./file0\x00')`,
+ `mutate9(&(0x7f0000000040)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00')`,
+ },
+ {
+ `mutate10(&(0x7f0000000000)=""/10)`,
+ `mutate10(&(0x7f0000000040)=""/256)`,
+ },
}
target := initTargetTest(t, "test", "64")
diff --git a/prog/rand.go b/prog/rand.go
index 24662f6ff..1db5cd07a 100644
--- a/prog/rand.go
+++ b/prog/rand.go
@@ -278,6 +278,8 @@ func escapingFilename(file string) bool {
var specialFiles = []string{"", "."}
+const specialFileLenPad = "a"
+
func (r *randGen) filenameImpl(s *state) string {
if r.oneOf(100) {
return specialFiles[r.Intn(len(specialFiles))]
@@ -296,6 +298,15 @@ func (r *randGen) filenameImpl(s *state) string {
}
for i := 0; ; i++ {
f := fmt.Sprintf("%v/file%v", dir, i)
+ if r.oneOf(100) {
+ // Make file name very long using target.SpecialFileLenghts consts.
+ // Add/subtract some small const to account for our file name prefix
+ // and potential kernel off-by-one's.
+ fileLen := r.randFilenameLength()
+ if add := fileLen - len(f); add > 0 {
+ f += strings.Repeat(specialFileLenPad, add)
+ }
+ }
if !s.files[f] {
return f
}
@@ -304,6 +315,19 @@ func (r *randGen) filenameImpl(s *state) string {
return r.randFromMap(s.files)
}
+func (r *randGen) randFilenameLength() int {
+ off := r.biasedRand(10, 5)
+ if r.bin() {
+ off = -off
+ }
+ lens := r.target.SpecialFileLenghts
+ res := lens[r.Intn(len(lens))] + off
+ if res < 0 {
+ res = 0
+ }
+ return res
+}
+
func (r *randGen) randFromMap(m map[string]bool) string {
files := make([]string, 0, len(m))
for f := range m {
@@ -721,10 +745,8 @@ func (a *BufferType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*
sz = a.Size()
case r.nOutOf(1, 3):
sz = r.rand(100)
- case r.nOutOf(1, 2):
- sz = 108 // UNIX_PATH_MAX
default:
- sz = 4096 // PATH_MAX
+ sz = uint64(r.randFilenameLength())
}
return MakeOutDataArg(a, dir, sz), nil
}
diff --git a/prog/rand_test.go b/prog/rand_test.go
index 030be8ec3..5d9b53d48 100644
--- a/prog/rand_test.go
+++ b/prog/rand_test.go
@@ -12,7 +12,11 @@ import (
)
func TestNotEscaping(t *testing.T) {
- r := newRand(nil, rand.NewSource(0))
+ target, err := GetTarget("test", "64")
+ if err != nil {
+ t.Fatal(err)
+ }
+ r := newRand(target, rand.NewSource(0))
s := &state{
files: map[string]bool{"./file0": true},
}
diff --git a/prog/target.go b/prog/target.go
index 08495f1f2..56c2a94ca 100644
--- a/prog/target.go
+++ b/prog/target.go
@@ -54,6 +54,9 @@ type Target struct {
// Additional special invalid pointer values besides NULL to use.
SpecialPointers []uint64
+ // Special file name length that can provoke bugs (e.g. PATH_MAX).
+ SpecialFileLenghts []int
+
// Filled by prog package:
SyscallMap map[string]*Syscall
ConstMap map[string]uint64
@@ -132,6 +135,15 @@ func (target *Target) lazyInit() {
if len(target.SpecialPointers) > maxSpecialPointers {
panic("too many special pointers")
}
+ if len(target.SpecialFileLenghts) == 0 {
+ // Just some common lengths that can be used as PATH_MAX/MAX_NAME.
+ target.SpecialFileLenghts = []int{256, 512, 4096}
+ }
+ for _, ln := range target.SpecialFileLenghts {
+ if ln <= 0 || ln >= memAllocMaxMem {
+ panic(fmt.Sprintf("bad special file length %v", ln))
+ }
+ }
// These are used only during lazyInit.
target.ConstMap = nil
target.types = nil
diff --git a/sys/linux/init.go b/sys/linux/init.go
index 6c486c2ed..480ac1d96 100644
--- a/sys/linux/init.go
+++ b/sys/linux/init.go
@@ -98,6 +98,18 @@ func InitTarget(target *prog.Target) {
panic("unknown arch")
}
+ target.SpecialFileLenghts = []int{
+ int(target.GetConst("PATH_MAX")),
+ int(target.GetConst("UNIX_PATH_MAX")),
+ int(target.GetConst("NAME_MAX")),
+ int(target.GetConst("BTRFS_INO_LOOKUP_PATH_MAX")),
+ int(target.GetConst("BTRFS_INO_LOOKUP_USER_PATH_MAX")),
+ int(target.GetConst("SMB_PATH_MAX")),
+ int(target.GetConst("XT_CGROUP_PATH_MAX")),
+ int(target.GetConst("XENSTORE_REL_PATH_MAX")),
+ 1 << 16, // gVisor's MaxFilenameLen
+ }
+
if target.Arch == runtime.GOARCH {
KCOV_INIT_TRACE = uintptr(target.GetConst("KCOV_INIT_TRACE"))
KCOV_ENABLE = uintptr(target.GetConst("KCOV_ENABLE"))
diff --git a/sys/linux/sys.txt b/sys/linux/sys.txt
index 342633a8d..fc04a4385 100644
--- a/sys/linux/sys.txt
+++ b/sys/linux/sys.txt
@@ -56,6 +56,9 @@ include <uapi/linux/seccomp.h>
include <uapi/linux/watch_queue.h>
include <kernel/sched/sched.h>
include <uapi/linux/close_range.h>
+include <uapi/linux/netfilter/xt_cgroup.h>
+include <fs/cifs/cifsglob.h>
+include <xen/interface/io/xs_wire.h>
resource fd[int32]: -1
resource fd_dir[fd]: AT_FDCWD
@@ -1319,6 +1322,9 @@ _ = STA_PLL, STA_PPSFREQ, STA_PPSTIME, STA_FLL, STA_INS, STA_DEL, STA_UNSYNC, ST
# clock_adjtime modes
_ = ADJ_OFFSET, ADJ_FREQUENCY, ADJ_MAXERROR, ADJ_ESTERROR, ADJ_STATUS, ADJ_TIMECONST, ADJ_TAI, ADJ_SETOFFSET, ADJ_MICRO, ADJ_NANO, ADJ_TICK, ADJ_OFFSET_SINGLESHOT
+# Consts for target.SpecialFileLenghts.
+_ = SMB_PATH_MAX, XT_CGROUP_PATH_MAX, XENSTORE_REL_PATH_MAX
+
# misc
_ = KCOV_INIT_TRACE, KCOV_ENABLE, KCOV_DISABLE, KCOV_TRACE_PC, KCOV_TRACE_CMP, PTRACE_TRACEME, SYSLOG_ACTION_CONSOLE_ON, SYSLOG_ACTION_CONSOLE_OFF, SYSLOG_ACTION_CONSOLE_LEVEL, SYSLOG_ACTION_CLEAR, __NR_mmap2
diff --git a/sys/linux/sys.txt.const b/sys/linux/sys.txt.const
index c977820f5..d918fdc4b 100644
--- a/sys/linux/sys.txt.const
+++ b/sys/linux/sys.txt.const
@@ -503,6 +503,7 @@ SIGINFO_SIZE = 128
SIG_BLOCK = 0, mips64le:1
SIG_SETMASK = 2, mips64le:3
SIG_UNBLOCK = 1, mips64le:2
+SMB_PATH_MAX = 260
SPLICE_F_GIFT = 8
SPLICE_F_MORE = 4
SPLICE_F_MOVE = 1
@@ -586,6 +587,8 @@ WNOHANG = 1
WNOWAIT = 16777216
WSTOPPED = 2
WUNTRACED = 2
+XENSTORE_REL_PATH_MAX = 2048
+XT_CGROUP_PATH_MAX = 512
_LINUX_CAPABILITY_VERSION_1 = 429392688
_LINUX_CAPABILITY_VERSION_2 = 537333798
_LINUX_CAPABILITY_VERSION_3 = 537396514
diff --git a/sys/test/init.go b/sys/test/init.go
index ee2281f70..1da6bbf52 100644
--- a/sys/test/init.go
+++ b/sys/test/init.go
@@ -10,4 +10,5 @@ import (
func InitTarget(target *prog.Target) {
target.MakeDataMmap = targets.MakeSyzMmap(target)
+ target.SpecialFileLenghts = []int{3, 256}
}
diff --git a/sys/test/test.txt b/sys/test/test.txt
index a8e0a39fd..c780d5fe9 100644
--- a/sys/test/test.txt
+++ b/sys/test/test.txt
@@ -747,6 +747,8 @@ mutate5(filename ptr[in, filename], flags flags[open_flags]) fd
mutate6(fd fd, data ptr[in, array[int8]], size bytesize[data])
mutate7(a0 ptr[in, string], a1 len[a0])
mutate8(a0 proc[100, 4, opt])
+mutate9(filename ptr[in, filename])
+mutate10(filename ptr[out, filename])
# Test for arguments mutation
mutate_integer(b1 bool8, b2 bool8, b3 bool8, b4 bool8, b5 bool8, b6 bool8, b7 bool8, b8 bool8, i9 int64)