diff options
| -rw-r--r-- | prog/hints_test.go | 8 | ||||
| -rw-r--r-- | prog/mutation.go | 6 | ||||
| -rw-r--r-- | prog/mutation_test.go | 31 | ||||
| -rw-r--r-- | prog/rand.go | 70 | ||||
| -rw-r--r-- | prog/rand_test.go | 21 | ||||
| -rw-r--r-- | prog/size.go | 4 | ||||
| -rw-r--r-- | prog/types.go | 18 |
7 files changed, 129 insertions, 29 deletions
diff --git a/prog/hints_test.go b/prog/hints_test.go index 78b86e57c..519bdd235 100644 --- a/prog/hints_test.go +++ b/prog/hints_test.go @@ -386,11 +386,11 @@ func TestHintsRandom(t *testing.T) { for i, c := range p.Calls { vals := extractValues(c) for j := 0; j < 5; j++ { - vals[r.randInt()] = true + vals[r.randInt64()] = true } comps := make(CompMap) for v := range vals { - comps.AddComp(v, r.randInt()) + comps.AddComp(v, r.randInt64()) } p.MutateWithHints(i, comps, func(p1 *Prog) {}) } @@ -489,11 +489,11 @@ func BenchmarkHints(b *testing.B) { for i, c := range p.Calls { vals := extractValues(c) for j := 0; j < 5; j++ { - vals[r.randInt()] = true + vals[r.randInt64()] = true } comps[i] = make(CompMap) for v := range vals { - comps[i].AddComp(v, r.randInt()) + comps[i].AddComp(v, r.randInt64()) } } b.RunParallel(func(pb *testing.PB) { diff --git a/prog/mutation.go b/prog/mutation.go index 1031217b4..b0702ebba 100644 --- a/prog/mutation.go +++ b/prog/mutation.go @@ -258,6 +258,7 @@ func mutateInt(r *randGen, s *state, arg Arg) (calls []*Call, retry, preserve bo if r.bin() { return regenerate(r, s, arg) } + bits := arg.Type().TypeBitSize() a := arg.(*ConstArg) switch { case r.nOutOf(1, 3): @@ -265,8 +266,9 @@ func mutateInt(r *randGen, s *state, arg Arg) (calls []*Call, retry, preserve bo case r.nOutOf(1, 2): a.Val -= uint64(r.Intn(4)) + 1 default: - a.Val ^= 1 << uint64(r.Intn(64)) + a.Val ^= 1 << uint64(r.Intn(int(bits))) } + a.Val = truncateToBitSize(a.Val, bits) return } @@ -706,7 +708,7 @@ var mutateDataFuncs = [...]func(r *randGen, data []byte, minLen, maxLen uint64) return data, false } i := r.Intn(len(data) - width + 1) - value := r.randInt() + value := r.randInt64() if r.oneOf(10) { value = swap64(value) } diff --git a/prog/mutation_test.go b/prog/mutation_test.go index 9ae807e0a..6d19300b4 100644 --- a/prog/mutation_test.go +++ b/prog/mutation_test.go @@ -152,6 +152,37 @@ func TestMutateArgument(t *testing.T) { } } +func TestSizeMutateArg(t *testing.T) { + target, rs, iters := initRandomTargetTest(t, "test", "64") + r := newRand(target, rs) + ct := target.BuildChoiceTable(nil, nil) + for i := 0; i < 100; i++ { + p := target.Generate(rs, 10, nil) + for it := 0; it < iters; it++ { + p1 := p.Clone() + ctx := &mutator{ + p: p1, + r: r, + ncalls: 10, + ct: ct, + corpus: nil, + } + ctx.mutateArg() + ForeachArg(p.Calls[0], func(arg Arg, ctx *ArgCtx) { + if _, ok := arg.Type().(*IntType); !ok { + return + } + bits := arg.Type().TypeBitSize() + limit := uint64(1<<bits - 1) + val := arg.(*ConstArg).Val + if val > limit { + t.Fatalf("Invalid argument value: %d. (arg size: %d; max value: %d)", val, arg.Size(), limit) + } + }) + } + } +} + func TestRandomChoice(t *testing.T) { t.Parallel() target, err := GetTarget("test", "64") diff --git a/prog/rand.go b/prog/rand.go index cbfc960a8..3306ced66 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -55,24 +55,44 @@ func (r *randGen) rand64() uint64 { return v } -// Some potentially interesting integers. -var specialInts = []uint64{ - 0, 1, 31, 32, 63, 64, 127, 128, - 129, 255, 256, 257, 511, 512, - 1023, 1024, 1025, 2047, 2048, 4095, 4096, - (1 << 15) - 1, (1 << 15), (1 << 15) + 1, - (1 << 16) - 1, (1 << 16), (1 << 16) + 1, - (1 << 31) - 1, (1 << 31), (1 << 31) + 1, - (1 << 32) - 1, (1 << 32), (1 << 32) + 1, -} - -func (r *randGen) randInt() uint64 { +var ( + // Some potentially interesting integers. + specialInts = []uint64{ + 0, 1, 31, 32, 63, 64, 127, 128, + 129, 255, 256, 257, 511, 512, + 1023, 1024, 1025, 2047, 2048, 4095, 4096, + (1 << 15) - 1, (1 << 15), (1 << 15) + 1, + (1 << 16) - 1, (1 << 16), (1 << 16) + 1, + (1 << 31) - 1, (1 << 31), (1 << 31) + 1, + (1 << 32) - 1, (1 << 32), (1 << 32) + 1, + } + // The indexes (exclusive) for the maximum specialInts values that fit in 1, 2, ... 8 bytes. + specialIntIndex [9]int +) + +func init() { + sort.Slice(specialInts, func(i, j int) bool { + return specialInts[i] < specialInts[j] + }) + for i := range specialIntIndex { + bitSize := uint64(8 * i) + specialIntIndex[i] = sort.Search(len(specialInts), func(i int) bool { + return specialInts[i]>>bitSize != 0 + }) + } +} + +func (r *randGen) randInt64() uint64 { + return r.randInt(64) +} + +func (r *randGen) randInt(bits uint64) uint64 { v := r.rand64() switch { case r.nOutOf(100, 182): v %= 10 - case r.nOutOf(50, 82): - v = specialInts[r.Intn(len(specialInts))] + case bits >= 8 && r.nOutOf(50, 82): + v = specialInts[r.Intn(specialIntIndex[bits/8])] case r.nOutOf(10, 32): v %= 256 case r.nOutOf(10, 22): @@ -87,14 +107,21 @@ func (r *randGen) randInt() uint64 { case r.nOutOf(5, 7): v = uint64(-int64(v)) default: - v <<= uint(r.Intn(63)) + v <<= uint(r.Intn(int(bits))) } - return v + return truncateToBitSize(v, bits) +} + +func truncateToBitSize(v, bitSize uint64) uint64 { + if bitSize == 0 || bitSize > 64 { + panic(fmt.Sprintf("invalid bitSize value: %d", bitSize)) + } + return v & uint64(1<<bitSize-1) } -func (r *randGen) randRangeInt(begin uint64, end uint64) uint64 { +func (r *randGen) randRangeInt(begin, end, bitSize uint64) uint64 { if r.oneOf(100) { - return r.randInt() + return r.randInt(bitSize) } return begin + (r.Uint64() % (end - begin + 1)) } @@ -710,7 +737,8 @@ func (a *ConstType) generate(r *randGen, s *state) (arg Arg, calls []*Call) { } func (a *IntType) generate(r *randGen, s *state) (arg Arg, calls []*Call) { - v := r.randInt() + bits := a.TypeBitSize() + v := r.randInt(bits) switch a.Kind { case IntFileoff: switch { @@ -719,10 +747,10 @@ func (a *IntType) generate(r *randGen, s *state) (arg Arg, calls []*Call) { case r.nOutOf(10, 11): v = r.rand(100) default: - v = r.randInt() + v = r.randInt(bits) } case IntRange: - v = r.randRangeInt(a.RangeBegin, a.RangeEnd) + v = r.randRangeInt(a.RangeBegin, a.RangeEnd, bits) } return MakeConstArg(a, v), nil } diff --git a/prog/rand_test.go b/prog/rand_test.go index d96d96260..20a305afe 100644 --- a/prog/rand_test.go +++ b/prog/rand_test.go @@ -70,3 +70,24 @@ func generateProg(t *testing.T, target *Target, rs rand.Source) *Prog { } return p } + +func TestSizeGenerateConstArg(t *testing.T) { + target, rs, iters := initRandomTargetTest(t, "test", "64") + r := newRand(target, rs) + for _, c := range target.Syscalls { + ForeachType(c, func(typ Type) { + if _, ok := typ.(*IntType); !ok { + return + } + bits := typ.TypeBitSize() + limit := uint64(1<<bits - 1) + for i := 0; i < iters; i++ { + newArg, _ := typ.generate(r, nil) + newVal := newArg.(*ConstArg).Val + if newVal > limit { + t.Fatalf("invalid generated value: %d. (arg bitsize: %d; max value: %d)", newVal, bits, limit) + } + } + }) + } +} diff --git a/prog/size.go b/prog/size.go index a134f0d10..c465a9171 100644 --- a/prog/size.go +++ b/prog/size.go @@ -181,9 +181,9 @@ func (r *randGen) mutateSize(arg *ConstArg, parent []Arg) bool { if r.bin() { // Small adjustment to trigger missed size checks. if arg.Val != 0 && r.bin() { - arg.Val = r.randRangeInt(0, arg.Val-1) + arg.Val = r.randRangeInt(0, arg.Val-1, arg.Type().TypeBitSize()) } else { - arg.Val = r.randRangeInt(arg.Val+1, arg.Val+1000) + arg.Val = r.randRangeInt(arg.Val+1, arg.Val+1000, arg.Type().TypeBitSize()) } return true } diff --git a/prog/types.go b/prog/types.go index 9a58a9eee..10540900a 100644 --- a/prog/types.go +++ b/prog/types.go @@ -56,6 +56,7 @@ type Type interface { Optional() bool Varlen() bool Size() uint64 + TypeBitSize() uint64 Format() BinaryFormat BitfieldOffset() uint64 BitfieldLength() uint64 @@ -104,6 +105,10 @@ func (t *TypeCommon) Size() uint64 { return t.TypeSize } +func (t *TypeCommon) TypeBitSize() uint64 { + panic("cannot get the bitsize for a non-integer type") +} + func (t *TypeCommon) Varlen() bool { return t.IsVarlen } @@ -189,6 +194,19 @@ func (t *IntTypeCommon) Format() BinaryFormat { return t.ArgFormat } +// Returns the size in bits for integers in binary format or 64 for string-formatted integers. The return +// value is used in computing limits and truncating other values. +func (t *IntTypeCommon) TypeBitSize() uint64 { + if t.ArgFormat != FormatNative && t.ArgFormat != FormatBigEndian { + // TODO: add special cases for mutation and generation of string-formatted integers. + return 64 + } + if t.BitfieldLen != 0 { + return t.BitfieldLen + } + return t.TypeSize * 8 +} + func (t *IntTypeCommon) BitfieldOffset() uint64 { return t.BitfieldOff } |
