aboutsummaryrefslogtreecommitdiffstats
path: root/prog
diff options
context:
space:
mode:
authorEthan Graham <ethangraham@google.com>2025-09-15 13:05:17 +0000
committerAleksandr Nogikh <nogikh@google.com>2025-09-22 09:11:54 +0000
commit6c7b65699dcfc2e93d2e7917f6b0e7bab99f2a26 (patch)
tree8e1083c4b07c45f2be11b5b924a1d5c64d0e6e77 /prog
parentf333ae936f1859d357b8efc744915fe7927a6c5d (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.go61
-rw-r--r--prog/rand.go15
-rw-r--r--prog/types.go4
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 {