aboutsummaryrefslogtreecommitdiffstats
path: root/prog
diff options
context:
space:
mode:
authorAlexander Potapenko <glider@google.com>2025-09-09 12:09:02 +0200
committerAlexander Potapenko <glider@google.com>2025-09-09 18:27:31 +0000
commit5ac84ab421465f8f15ac9350f9f33a4416b4b3b7 (patch)
tree4bd25bbaef09bb2a4fa31877862e8d2f20b17d1f /prog
parentd291dd2d58a1885c00a60561048b6ceb1bf1206a (diff)
prog: pkg/compiler: docs: introduce the `no_squash` attribute
The `no_squash` per-syscall attribute prevents the fuzzer from generating squashed arguments to a particular syscall. This is particularly helpful for pseudo-syscalls with elaborate arguments that are hard to reason about when they are squashed - e.g. for syz_kvm_add_vcpu() that takes a SYZOS program as an input. I've considered an alternative solution that prohibits ANY for all pseudo-syscalls. But there is a bunch of existing programs (both the tests and the repros) for syscalls like syz_mount_image() for which the benefit of not passing ANY is not immediately obvious. I therefore decided to go with an explicit attribute that can later be enforced for every pseudo-syscall at compile time.
Diffstat (limited to 'prog')
-rw-r--r--prog/mutation.go3
-rw-r--r--prog/mutation_test.go26
-rw-r--r--prog/types.go1
-rw-r--r--prog/validation.go16
4 files changed, 41 insertions, 5 deletions
diff --git a/prog/mutation.go b/prog/mutation.go
index eb9e8285d..4d05e0a7d 100644
--- a/prog/mutation.go
+++ b/prog/mutation.go
@@ -145,6 +145,9 @@ func (ctx *mutator) squashAny() bool {
if ctx.noMutate[ptr.call.Meta.ID] {
return false
}
+ if ptr.call.Meta.Attrs.NoSquash {
+ return false
+ }
if !p.Target.isAnyPtr(ptr.arg.Type()) {
p.Target.squashPtr(ptr.arg)
}
diff --git a/prog/mutation_test.go b/prog/mutation_test.go
index 1cd18656e..d9734b482 100644
--- a/prog/mutation_test.go
+++ b/prog/mutation_test.go
@@ -165,6 +165,32 @@ func TestMutateArgument(t *testing.T) {
}
}
+func TestMutateNoSquash(t *testing.T) {
+ target := initTargetTest(t, "test", "64")
+ p, err := target.Deserialize([]byte(`mutate_no_squash(&(0x7f0000000000)={0x1, 0x2, 0x3, 0x4, "5e9ce23b"})`), Strict)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rs := rand.NewSource(0)
+ r := newRand(target, rs)
+ ctx := &mutator{
+ p: p,
+ r: r,
+ ncalls: 1,
+ ct: target.DefaultChoiceTable(),
+ opts: DefaultMutateOpts,
+ }
+
+ // squashAny should not mutate the program.
+ for i := 0; i < 100; i++ {
+ p1 := p.Clone()
+ ctx.p = p1
+ if ctx.squashAny() {
+ t.Fatalf("squashAny mutated a no_squash call: %s", p1.Serialize())
+ }
+ }
+}
+
func TestSizeMutateArg(t *testing.T) {
target, rs, iters := initRandomTargetTest(t, "test", "64")
r := newRand(target, rs)
diff --git a/prog/types.go b/prog/types.go
index 3bf7d640e..5a170dad1 100644
--- a/prog/types.go
+++ b/prog/types.go
@@ -44,6 +44,7 @@ type SyscallAttrs struct {
BreaksReturns bool
NoGenerate bool
NoMinimize bool
+ NoSquash bool
RemoteCover bool
Automatic bool
AutomaticHelper bool
diff --git a/prog/validation.go b/prog/validation.go
index 48a6efade..3fec567b9 100644
--- a/prog/validation.go
+++ b/prog/validation.go
@@ -31,11 +31,12 @@ func (p *Prog) validate() error {
}
type validCtx struct {
- target *Target
- isUnsafe bool
- opts validationOptions
- args map[Arg]bool
- uses map[Arg]Arg
+ target *Target
+ isUnsafe bool
+ opts validationOptions
+ args map[Arg]bool
+ uses map[Arg]Arg
+ currentCall *Call
}
type validationOptions struct {
@@ -54,10 +55,12 @@ func (p *Prog) validateWithOpts(opts validationOptions) error {
if c.Meta == nil {
return fmt.Errorf("call does not have meta information")
}
+ ctx.currentCall = c
if err := ctx.validateCall(c); err != nil {
return fmt.Errorf("call #%d %v: %w", i, c.Meta.Name, err)
}
}
+ ctx.currentCall = nil
for u, orig := range ctx.uses {
if !ctx.args[u] {
return fmt.Errorf("use of %+v referes to an out-of-tree arg\narg: %#v", orig, u)
@@ -128,6 +131,9 @@ func (ctx *validCtx) validateArg(arg Arg, typ Type, dir Dir) error {
if !ctx.target.isAnyPtr(arg.Type()) && arg.Type() != typ {
return fmt.Errorf("bad arg type %#v, expect %#v", arg.Type(), typ)
}
+ if ctx.currentCall.Meta.Attrs.NoSquash && ctx.target.isAnyPtr(arg.Type()) {
+ return fmt.Errorf("ANY argument for no_squash call %v", ctx.currentCall.Meta.Name)
+ }
ctx.args[arg] = true
return arg.validate(ctx, dir)
}