diff options
| author | Ethan Graham <ethangraham@google.com> | 2025-09-15 13:05:17 +0000 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2025-09-22 09:11:54 +0000 |
| commit | 6c7b65699dcfc2e93d2e7917f6b0e7bab99f2a26 (patch) | |
| tree | 8e1083c4b07c45f2be11b5b924a1d5c64d0e6e77 /prog | |
| parent | f333ae936f1859d357b8efc744915fe7927a6c5d (diff) | |
prog: add specialized mutation for KFuzzTest calls
Internal kernel functions (and as a result KFuzzTest) have stricter
contracts than system calls. For this reason, we must avoid mutating
the following cases:
- Length arguments not matching the length of the related buffer.
- Strings not being null-terminated.
Add special cases for KFuzzTest calls that avoids these situations.
Signed-off-by: Ethan Graham <ethangraham@google.com>
Diffstat (limited to 'prog')
| -rw-r--r-- | prog/mutation.go | 61 | ||||
| -rw-r--r-- | prog/rand.go | 15 | ||||
| -rw-r--r-- | prog/types.go | 4 |
3 files changed, 60 insertions, 20 deletions
diff --git a/prog/mutation.go b/prog/mutation.go index 4d05e0a7d..ecae81608 100644 --- a/prog/mutation.go +++ b/prog/mutation.go @@ -227,13 +227,20 @@ func (ctx *mutator) mutateArg() bool { return false } c := p.Calls[idx] + if c.Meta.Attrs.KFuzzTest { + tmp := r.genKFuzzTest + r.genKFuzzTest = true + defer func() { + r.genKFuzzTest = tmp + }() + } if ctx.noMutate[c.Meta.ID] { return false } updateSizes := true for stop, ok := false, false; !stop; stop = ok && r.oneOf(ctx.opts.MutateArgCount) { ok = true - ma := &mutationArgs{target: p.Target} + ma := &mutationArgs{target: p.Target, ignoreLengths: c.Meta.Attrs.KFuzzTest} ForeachArg(c, ma.collectArg) if len(ma.args) == 0 { return false @@ -271,7 +278,7 @@ func chooseCall(p *Prog, r *randGen) int { for _, c := range p.Calls { var totalPrio float64 ForeachArg(c, func(arg Arg, ctx *ArgCtx) { - prio, stopRecursion := arg.Type().getMutationPrio(p.Target, arg, false) + prio, stopRecursion := arg.Type().getMutationPrio(p.Target, arg, false, c.Meta.Attrs.KFuzzTest) totalPrio += prio ctx.Stop = stopRecursion }) @@ -509,7 +516,10 @@ func (t *ArrayType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []* func (t *PtrType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) { a := arg.(*PointerArg) - if r.oneOf(1000) { + // Do not generate special pointers for KFuzzTest calls, as they are + // difficult to identify in the kernel and can lead to false positive + // crash reports. + if r.oneOf(1000) && !r.genKFuzzTest { removeArg(a.Res) index := r.rand(len(r.target.SpecialPointers)) newArg := MakeSpecialPointerArg(t, a.Dir(), index) @@ -565,6 +575,7 @@ func (t *ConstType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []* type mutationArgs struct { target *Target ignoreSpecial bool + ignoreLengths bool prioSum float64 args []mutationArg argsBuffer [16]mutationArg @@ -587,7 +598,7 @@ func (ma *mutationArgs) collectArg(arg Arg, ctx *ArgCtx) { ma.ignoreSpecial = false typ := arg.Type() - prio, stopRecursion := typ.getMutationPrio(ma.target, arg, ignoreSpecial) + prio, stopRecursion := typ.getMutationPrio(ma.target, arg, ignoreSpecial, ma.ignoreLengths) ctx.Stop = stopRecursion if prio == dontMutate { @@ -617,7 +628,8 @@ func (ma *mutationArgs) chooseArg(r *rand.Rand) (Arg, ArgCtx) { // 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) { +func (t *IntType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths 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 @@ -650,14 +662,16 @@ func (t *IntType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) ( return prio, false } -func (t *StructType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { +func (t *StructType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) { if target.SpecialTypes[t.Name()] == nil || ignoreSpecial { return dontMutate, false } return maxPriority, true } -func (t *UnionType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { +func (t *UnionType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) { if target.SpecialTypes[t.Name()] == nil && len(t.Fields) == 1 || ignoreSpecial { return dontMutate, false } @@ -669,7 +683,8 @@ func (t *UnionType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) return maxPriority, true } -func (t *FlagsType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { +func (t *FlagsType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) { prio = rangeSizePrio(uint64(len(t.Vals))) if t.BitMask { // We want a higher priority because the mutation will include @@ -694,7 +709,8 @@ func rangeSizePrio(size uint64) (prio float64) { return prio } -func (t *PtrType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { +func (t *PtrType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) { if arg.(*PointerArg).IsSpecial() { // TODO: we ought to mutate this, but we don't have code for this yet. return dontMutate, false @@ -702,32 +718,42 @@ func (t *PtrType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) ( return 0.3 * maxPriority, false } -func (t *ConstType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { +func (t *ConstType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) { return dontMutate, false } -func (t *CsumType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { +func (t *CsumType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) { return dontMutate, false } -func (t *ProcType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { +func (t *ProcType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) { return 0.5 * maxPriority, false } -func (t *ResourceType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { +func (t *ResourceType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) { return 0.5 * maxPriority, false } -func (t *VmaType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { +func (t *VmaType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) { return 0.5 * maxPriority, false } -func (t *LenType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { +func (t *LenType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) { // Mutating LenType only produces "incorrect" results according to descriptions. + if ignoreLengths { + return dontMutate, false + } return 0.1 * maxPriority, false } -func (t *BufferType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { +func (t *BufferType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) { if arg.Dir() == DirOut && !t.Varlen() { return dontMutate, false } @@ -742,7 +768,8 @@ func (t *BufferType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool return 0.8 * maxPriority, false } -func (t *ArrayType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) { +func (t *ArrayType) getMutationPrio(target *Target, arg Arg, + ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) { if t.Kind == ArrayRangeLen && t.RangeBegin == t.RangeEnd { return dontMutate, false } diff --git a/prog/rand.go b/prog/rand.go index 957cf7112..b06cc1a90 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -28,6 +28,7 @@ type randGen struct { target *Target inGenerateResource bool patchConditionalDepth int + genKFuzzTest bool recDepth map[string]int } @@ -354,7 +355,9 @@ func (r *randGen) randString(s *state, t *BufferType) []byte { buf.Write([]byte{byte(r.Intn(256))}) } } - if r.oneOf(100) == t.NoZ { + // We always null-terminate strings that are inputs to KFuzzTest calls to + // avoid false-positive buffer overflow reports. + if r.oneOf(100) == t.NoZ || r.genKFuzzTest { buf.Write([]byte{0}) } return buf.Bytes() @@ -609,6 +612,16 @@ func (r *randGen) generateParticularCall(s *state, meta *Syscall) (calls []*Call panic(fmt.Sprintf("generating no_generate call: %v", meta.Name)) } c := MakeCall(meta, nil) + // KFuzzTest calls restrict mutation and generation. Since calls to + // generateParticularCall can be recursive, we save the previous value, and + // set it true. + if c.Meta.Attrs.KFuzzTest { + tmp := r.genKFuzzTest + r.genKFuzzTest = true + defer func() { + r.genKFuzzTest = tmp + }() + } c.Args, calls = r.generateArgs(s, meta.Args, DirIn) moreCalls, _ := r.patchConditionalFields(c, s) r.target.assignSizesCall(c) diff --git a/prog/types.go b/prog/types.go index cb64603fe..2329c348f 100644 --- a/prog/types.go +++ b/prog/types.go @@ -194,7 +194,7 @@ type Type interface { isDefaultArg(arg Arg) bool generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) - getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) + getMutationPrio(target *Target, arg Arg, ignoreSpecial, ignoreLengths bool) (prio float64, stopRecursion bool) minimize(ctx *minimizeArgsCtx, arg Arg, path string) bool ref() Ref setRef(ref Ref) @@ -224,7 +224,7 @@ func (ti Ref) generate(r *randGen, s *state, dir Dir) (Arg, []*Call) { panic("pr func (ti Ref) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) ([]*Call, bool, bool) { panic("prog.Ref method called") } -func (ti Ref) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (float64, bool) { +func (ti Ref) getMutationPrio(target *Target, arg Arg, ignoreSpecial, ignoreLengths bool) (float64, bool) { panic("prog.Ref method called") } func (ti Ref) minimize(ctx *minimizeArgsCtx, arg Arg, path string) bool { |
