// Copyright 2015 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. package prog import ( "encoding/binary" "fmt" "math" "math/rand" "sort" ) // Maximum length of generated binary blobs inserted into the program. const maxBlobLen = uint64(100 << 10) // Mutate program p. // // p: The program to mutate. // rs: Random source. // ncalls: The allowed maximum calls in mutated program. // ct: ChoiceTable for syscalls. // corpus: The entire corpus, including original program p. func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable, corpus []*Prog) { r := newRand(p.Target, rs) if ncalls < len(p.Calls) { ncalls = len(p.Calls) } ctx := &mutator{ p: p, r: r, ncalls: ncalls, ct: ct, corpus: corpus, } for stop, ok := false, false; !stop; stop = ok && len(p.Calls) != 0 && r.oneOf(3) { switch { case r.oneOf(5): // Not all calls have anything squashable, // so this has lower priority in reality. ok = ctx.squashAny() case r.nOutOf(1, 100): ok = ctx.splice() case r.nOutOf(20, 31): ok = ctx.insertCall() case r.nOutOf(10, 11): ok = ctx.mutateArg() default: ok = ctx.removeCall() } } p.sanitizeFix() p.debugValidate() if got := len(p.Calls); got < 1 || got > ncalls { panic(fmt.Sprintf("bad number of calls after mutation: %v, want [1, %v]", got, ncalls)) } } // Internal state required for performing mutations -- currently this matches // the arguments passed to Mutate(). type mutator struct { p *Prog // The program to mutate. r *randGen // The randGen instance. ncalls int // The allowed maximum calls in mutated program. ct *ChoiceTable // ChoiceTable for syscalls. corpus []*Prog // The entire corpus, including original program p. } // This function selects a random other program p0 out of the corpus, and // mutates ctx.p as follows: preserve ctx.p's Calls up to a random index i // (exclusive) concatenated with p0's calls from index i (inclusive). func (ctx *mutator) splice() bool { p, r := ctx.p, ctx.r if len(ctx.corpus) == 0 || len(p.Calls) == 0 || len(p.Calls) >= ctx.ncalls { return false } p0 := ctx.corpus[r.Intn(len(ctx.corpus))] p0c := p0.Clone() idx := r.Intn(len(p.Calls)) p.Calls = append(p.Calls[:idx], append(p0c.Calls, p.Calls[idx:]...)...) for i := len(p.Calls) - 1; i >= ctx.ncalls; i-- { p.removeCall(i) } return true } // Picks a random complex pointer and squashes its arguments into an ANY. // Subsequently, if the ANY contains blobs, mutates a random blob. func (ctx *mutator) squashAny() bool { p, r := ctx.p, ctx.r complexPtrs := p.complexPtrs() if len(complexPtrs) == 0 { return false } ptr := complexPtrs[r.Intn(len(complexPtrs))] if !p.Target.isAnyPtr(ptr.Type()) { p.Target.squashPtr(ptr) } var blobs []*DataArg var bases []*PointerArg ForeachSubArg(ptr, func(arg Arg, ctx *ArgCtx) { if data, ok := arg.(*DataArg); ok && arg.Dir() != DirOut { blobs = append(blobs, data) bases = append(bases, ctx.Base) } }) if len(blobs) == 0 { return false } // TODO(dvyukov): we probably want special mutation for ANY. // E.g. merging adjacent ANYBLOBs (we don't create them, // but they can appear in future); or replacing ANYRES // with a blob (and merging it with adjacent blobs). idx := r.Intn(len(blobs)) arg := blobs[idx] base := bases[idx] baseSize := base.Res.Size() arg.data = mutateData(r, arg.Data(), 0, maxBlobLen) // Update base pointer if size has increased. if baseSize < base.Res.Size() { s := analyze(ctx.ct, ctx.corpus, p, p.Calls[0]) newArg := r.allocAddr(s, base.Type(), base.Dir(), base.Res.Size(), base.Res) *base = *newArg } return true } // Inserts a new call at a randomly chosen point (with bias towards the end of // existing program). Does not insert a call if program already has ncalls. func (ctx *mutator) insertCall() bool { p, r := ctx.p, ctx.r if len(p.Calls) >= ctx.ncalls { return false } idx := r.biasedRand(len(p.Calls)+1, 5) var c *Call if idx < len(p.Calls) { c = p.Calls[idx] } s := analyze(ctx.ct, ctx.corpus, p, c) calls := r.generateCall(s, p, idx) p.insertBefore(c, calls) for len(p.Calls) > ctx.ncalls { p.removeCall(idx) } return true } // Removes a random call from program. func (ctx *mutator) removeCall() bool { p, r := ctx.p, ctx.r if len(p.Calls) == 0 { return false } idx := r.Intn(len(p.Calls)) p.removeCall(idx) return true } // Mutate an argument of a random call. func (ctx *mutator) mutateArg() bool { p, r := ctx.p, ctx.r if len(p.Calls) == 0 { return false } idx := chooseCall(p, r) if idx < 0 { return false } c := p.Calls[idx] updateSizes := true for stop, ok := false, false; !stop; stop = ok && r.oneOf(3) { ok = true ma := &mutationArgs{target: p.Target} ForeachArg(c, ma.collectArg) if len(ma.args) == 0 { return false } s := analyze(ctx.ct, ctx.corpus, p, c) arg, argCtx := ma.chooseArg(r.Rand) calls, ok1 := p.Target.mutateArg(r, s, arg, argCtx, &updateSizes) if !ok1 { ok = false continue } p.insertBefore(c, calls) idx += len(calls) for len(p.Calls) > ctx.ncalls { idx-- p.removeCall(idx) } if idx < 0 || idx >= len(p.Calls) || p.Calls[idx] != c { panic(fmt.Sprintf("wrong call index: idx=%v calls=%v p.Calls=%v ncalls=%v", idx, len(calls), len(p.Calls), ctx.ncalls)) } if updateSizes { p.Target.assignSizesCall(c) } } return true } // Select a call based on the complexity of the arguments. func chooseCall(p *Prog, r *randGen) int { var prioSum float64 var callPriorities []float64 for _, c := range p.Calls { var totalPrio float64 ForeachArg(c, func(arg Arg, ctx *ArgCtx) { prio, stopRecursion := arg.Type().getMutationPrio(p.Target, arg, false) totalPrio += prio ctx.Stop = stopRecursion }) prioSum += totalPrio callPriorities = append(callPriorities, prioSum) } if prioSum == 0 { return -1 // All calls are without arguments. } return sort.SearchFloat64s(callPriorities, prioSum*r.Float64()) } func (target *Target) mutateArg(r *randGen, s *state, arg Arg, ctx ArgCtx, updateSizes *bool) ([]*Call, bool) { var baseSize uint64 if ctx.Base != nil { baseSize = ctx.Base.Res.Size() } calls, retry, preserve := arg.Type().mutate(r, s, arg, ctx) if retry { return nil, false } if preserve { *updateSizes = false } // Update base pointer if size has increased. if base := ctx.Base; base != nil && baseSize < base.Res.Size() { newArg := r.allocAddr(s, base.Type(), base.Dir(), base.Res.Size(), base.Res) replaceArg(base, newArg) } return calls, true } func regenerate(r *randGen, s *state, arg Arg) (calls []*Call, retry, preserve bool) { var newArg Arg newArg, calls = r.generateArg(s, arg.Type(), arg.Dir()) replaceArg(arg, newArg) return } func mutateInt(r *randGen, a *ConstArg, t *IntType) uint64 { switch { case r.nOutOf(1, 3): return a.Val + (uint64(r.Intn(4)) + 1) case r.nOutOf(1, 2): return a.Val - (uint64(r.Intn(4)) + 1) default: return a.Val ^ (1 << uint64(r.Intn(int(t.TypeBitSize())))) } } func mutateAlignedInt(r *randGen, a *ConstArg, t *IntType) uint64 { rangeEnd := t.RangeEnd if t.RangeBegin == 0 && int64(rangeEnd) == -1 { // Special [0:-1] range for all possible values. rangeEnd = uint64(1< maxLen { arg.size = maxLen } } } func (t *ArrayType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) { // TODO: swap elements of the array a := arg.(*GroupArg) count := uint64(0) switch t.Kind { case ArrayRandLen: if r.bin() { for count = uint64(len(a.Inner)); r.bin(); { count++ } } else { for count == uint64(len(a.Inner)) { count = r.randArrayLen() } } case ArrayRangeLen: if t.RangeBegin == t.RangeEnd { panic("trying to mutate fixed length array") } for count == uint64(len(a.Inner)) { count = r.randRange(t.RangeBegin, t.RangeEnd) } } if count > uint64(len(a.Inner)) { for count > uint64(len(a.Inner)) { newArg, newCalls := r.generateArg(s, t.Elem, a.Dir()) a.Inner = append(a.Inner, newArg) calls = append(calls, newCalls...) for _, c := range newCalls { s.analyze(c) } } } else if count < uint64(len(a.Inner)) { for _, arg := range a.Inner[count:] { removeArg(arg) } a.Inner = a.Inner[:count] } return } func (t *PtrType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) { a := arg.(*PointerArg) if r.oneOf(1000) { removeArg(a.Res) index := r.rand(len(r.target.SpecialPointers)) newArg := MakeSpecialPointerArg(t, a.Dir(), index) replaceArg(arg, newArg) return } newArg := r.allocAddr(s, t, a.Dir(), a.Res.Size(), a.Res) replaceArg(arg, newArg) return } func (t *StructType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) { gen := r.target.SpecialTypes[t.Name()] if gen == nil { panic("bad arg returned by mutationArgs: StructType") } var newArg Arg newArg, calls = gen(&Gen{r, s}, t, arg.Dir(), arg) a := arg.(*GroupArg) for i, f := range newArg.(*GroupArg).Inner { replaceArg(a.Inner[i], f) } return } func (t *UnionType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) { if gen := r.target.SpecialTypes[t.Name()]; gen != nil { var newArg Arg newArg, calls = gen(&Gen{r, s}, t, arg.Dir(), arg) replaceArg(arg, newArg) return } a := arg.(*UnionArg) index := r.Intn(len(t.Fields) - 1) if index >= a.Index { index++ } optType := t.Fields[index].Type removeArg(a.Option) var newOpt Arg newOpt, calls = r.generateArg(s, optType, a.Dir()) replaceArg(arg, MakeUnionArg(t, a.Dir(), newOpt, index)) return } func (t *CsumType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) { panic("CsumType can't be mutated") } func (t *ConstType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) { panic("ConstType can't be mutated") } type mutationArgs struct { target *Target ignoreSpecial bool prioSum float64 args []mutationArg argsBuffer [16]mutationArg } type mutationArg struct { arg Arg ctx ArgCtx priority float64 } const ( maxPriority = float64(10) minPriority = float64(1) dontMutate = float64(0) ) func (ma *mutationArgs) collectArg(arg Arg, ctx *ArgCtx) { ignoreSpecial := ma.ignoreSpecial ma.ignoreSpecial = false typ := arg.Type() prio, stopRecursion := typ.getMutationPrio(ma.target, arg, ignoreSpecial) ctx.Stop = stopRecursion if prio == dontMutate { return } _, isArrayTyp := typ.(*ArrayType) _, isBufferTyp := typ.(*BufferType) if !isBufferTyp && !isArrayTyp && arg.Dir() == DirOut || !typ.Varlen() && typ.Size() == 0 { return } if len(ma.args) == 0 { ma.args = ma.argsBuffer[:0] } ma.prioSum += prio ma.args = append(ma.args, mutationArg{arg, *ctx, ma.prioSum}) } func (ma *mutationArgs) chooseArg(r *rand.Rand) (Arg, ArgCtx) { goal := ma.prioSum * r.Float64() chosenIdx := sort.Search(len(ma.args), func(i int) bool { return ma.args[i].priority >= goal }) arg := ma.args[chosenIdx] return arg.arg, arg.ctx } // TODO: find a way to estimate optimal priority values. // Assign a priority for each type. The boolean is the reference type and it has // the minimum priority, since it has only two possible values. func (t *IntType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { // For a integer without a range of values, the priority is based on // the number of bits occupied by the underlying type. plainPrio := math.Log2(float64(t.TypeBitSize())) + 0.1*maxPriority if t.Kind != IntRange { return plainPrio, false } size := t.RangeEnd - t.RangeBegin + 1 if t.Align != 0 { if t.RangeBegin == 0 && int64(t.RangeEnd) == -1 { // Special [0:-1] range for all possible values. size = (1<= maxLen { return data, false } n := r.Intn(16) + 1 if r := int(maxLen) - len(data); n > r { n = r } pos := r.Intn(len(data)) for i := 0; i < n; i++ { data = append(data, 0) } copy(data[pos+n:], data[pos:]) for i := 0; i < n; i++ { data[pos+i] = byte(r.Int31()) } if uint64(len(data)) > maxLen || r.bin() { data = data[:len(data)-n] // preserve original length } return data, true }, // Remove bytes. func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool) { if len(data) == 0 { return data, false } n := r.Intn(16) + 1 if n > len(data) { n = len(data) } pos := 0 if n < len(data) { pos = r.Intn(len(data) - n) } copy(data[pos:], data[pos+n:]) data = data[:len(data)-n] if uint64(len(data)) < minLen || r.bin() { for i := 0; i < n; i++ { data = append(data, 0) // preserve original length } } return data, true }, // Append a bunch of bytes. func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool) { if uint64(len(data)) >= maxLen { return data, false } const max = 256 n := max - r.biasedRand(max, 10) if r := int(maxLen) - len(data); n > r { n = r } for i := 0; i < n; i++ { data = append(data, byte(r.rand(256))) } return data, true }, // Replace int8/int16/int32/int64 with a random value. func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool) { width := 1 << uint(r.Intn(4)) if len(data) < width { return data, false } i := r.Intn(len(data) - width + 1) storeInt(data[i:], r.Uint64(), width) return data, true }, // Add/subtract from an int8/int16/int32/int64. func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool) { width := 1 << uint(r.Intn(4)) if len(data) < width { return data, false } i := r.Intn(len(data) - width + 1) v := loadInt(data[i:], width) delta := r.rand(2*maxDelta+1) - maxDelta if delta == 0 { delta = 1 } if r.oneOf(10) { v = swapInt(v, width) v += delta v = swapInt(v, width) } else { v += delta } storeInt(data[i:], v, width) return data, true }, // Set int8/int16/int32/int64 to an interesting value. func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool) { width := 1 << uint(r.Intn(4)) if len(data) < width { return data, false } i := r.Intn(len(data) - width + 1) value := r.randInt64() if r.oneOf(10) { value = swap64(value) } storeInt(data[i:], value, width) return data, true }, } func swap16(v uint16) uint16 { v0 := byte(v >> 0) v1 := byte(v >> 8) v = 0 v |= uint16(v1) << 0 v |= uint16(v0) << 8 return v } func swap32(v uint32) uint32 { v0 := byte(v >> 0) v1 := byte(v >> 8) v2 := byte(v >> 16) v3 := byte(v >> 24) v = 0 v |= uint32(v3) << 0 v |= uint32(v2) << 8 v |= uint32(v1) << 16 v |= uint32(v0) << 24 return v } func swap64(v uint64) uint64 { v0 := byte(v >> 0) v1 := byte(v >> 8) v2 := byte(v >> 16) v3 := byte(v >> 24) v4 := byte(v >> 32) v5 := byte(v >> 40) v6 := byte(v >> 48) v7 := byte(v >> 56) v = 0 v |= uint64(v7) << 0 v |= uint64(v6) << 8 v |= uint64(v5) << 16 v |= uint64(v4) << 24 v |= uint64(v3) << 32 v |= uint64(v2) << 40 v |= uint64(v1) << 48 v |= uint64(v0) << 56 return v } func swapInt(v uint64, size int) uint64 { switch size { case 1: return v case 2: return uint64(swap16(uint16(v))) case 4: return uint64(swap32(uint32(v))) case 8: return swap64(v) default: panic(fmt.Sprintf("swapInt: bad size %v", size)) } } func loadInt(data []byte, size int) uint64 { switch size { case 1: return uint64(data[0]) case 2: return uint64(binary.LittleEndian.Uint16(data)) case 4: return uint64(binary.LittleEndian.Uint32(data)) case 8: return binary.LittleEndian.Uint64(data) default: panic(fmt.Sprintf("loadInt: bad size %v", size)) } } func storeInt(data []byte, v uint64, size int) { switch size { case 1: data[0] = uint8(v) case 2: binary.LittleEndian.PutUint16(data, uint16(v)) case 4: binary.LittleEndian.PutUint32(data, uint32(v)) case 8: binary.LittleEndian.PutUint64(data, v) default: panic(fmt.Sprintf("storeInt: bad size %v", size)) } }