diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2022-08-09 12:40:58 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2022-08-10 11:45:49 +0200 |
| commit | 8fad22bf840fc968171bee6cf294f026eb1e0d3a (patch) | |
| tree | 2bad644606904f4e75ef6fe689bac49e91877c40 | |
| parent | a12254451cfa5e5129a0c091255d502b6805adf6 (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.go | 60 | ||||
| -rw-r--r-- | prog/minimization_test.go | 12 | ||||
| -rw-r--r-- | prog/mutation.go | 9 | ||||
| -rw-r--r-- | prog/mutation_test.go | 10 | ||||
| -rw-r--r-- | prog/rand.go | 28 | ||||
| -rw-r--r-- | prog/rand_test.go | 6 | ||||
| -rw-r--r-- | prog/target.go | 12 | ||||
| -rw-r--r-- | sys/linux/init.go | 12 | ||||
| -rw-r--r-- | sys/linux/sys.txt | 6 | ||||
| -rw-r--r-- | sys/linux/sys.txt.const | 3 | ||||
| -rw-r--r-- | sys/test/init.go | 1 | ||||
| -rw-r--r-- | sys/test/test.txt | 2 |
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) |
