aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--executor/defs.h2
-rw-r--r--executor/syscalls.h2
-rw-r--r--prog/mutation.go212
-rw-r--r--prog/mutation_test.go240
-rw-r--r--prog/types.go1
-rw-r--r--sys/test/gen/64.go8
-rw-r--r--sys/test/test.txt2
7 files changed, 389 insertions, 78 deletions
diff --git a/executor/defs.h b/executor/defs.h
index 21b54c955..2df5b419a 100644
--- a/executor/defs.h
+++ b/executor/defs.h
@@ -175,7 +175,7 @@
#if GOARCH_64
#define GOARCH "64"
-#define SYZ_REVISION "aaee55a3532998a140b25dc52cc113c4de048dd2"
+#define SYZ_REVISION "71d8f9a8f2ed06129920bb9f60aef16fb9ca749b"
#define SYZ_EXECUTOR_USES_FORK_SERVER 0
#define SYZ_EXECUTOR_USES_SHMEM 0
#define SYZ_PAGE_SIZE 4096
diff --git a/executor/syscalls.h b/executor/syscalls.h
index 12934de75..0a6286814 100644
--- a/executor/syscalls.h
+++ b/executor/syscalls.h
@@ -16307,11 +16307,13 @@ const call_t syscalls[] = {
{"foo$fmt5", 0},
{"minimize$0", 0},
{"mutate$array", 0},
+ {"mutate$array2", 0},
{"mutate$flags", 0},
{"mutate$flags2", 0},
{"mutate$flags3", 0},
{"mutate$integer", 0},
{"mutate$integer2", 0},
+ {"mutate$union", 0},
{"mutate0", 0},
{"mutate1", 0},
{"mutate2", 0},
diff --git a/prog/mutation.go b/prog/mutation.go
index 6c356c4b4..1031217b4 100644
--- a/prog/mutation.go
+++ b/prog/mutation.go
@@ -5,7 +5,9 @@ package prog
import (
"fmt"
+ "math"
"math/rand"
+ "sort"
"unsafe"
)
@@ -155,8 +157,9 @@ func (ctx *mutator) mutateArg() bool {
if len(p.Calls) == 0 {
return false
}
- c := p.Calls[r.Intn(len(p.Calls))]
- if len(c.Args) == 0 {
+
+ c, ok := chooseCall(p, r)
+ if !ok {
return false
}
s := analyze(ctx.ct, ctx.corpus, p, c)
@@ -168,8 +171,8 @@ func (ctx *mutator) mutateArg() bool {
if len(ma.args) == 0 {
return false
}
- idx := r.Intn(len(ma.args))
- arg, ctx := ma.args[idx], ma.ctxes[idx]
+ chosenIdx := randomChoice(ma.priorities, r)
+ arg, ctx := ma.args[chosenIdx], ma.ctxes[chosenIdx]
calls, ok1 := p.Target.mutateArg(r, s, arg, ctx, &updateSizes)
if !ok1 {
ok = false
@@ -184,6 +187,43 @@ func (ctx *mutator) mutateArg() bool {
return true
}
+// Select a call based on the complexity of the arguments.
+func chooseCall(p *Prog, r *randGen) (*Call, bool) {
+ var callPriorities []float64
+ noArgs := true
+
+ for _, c := range p.Calls {
+ totalPrio := float64(0)
+ ForeachArg(c, func(arg Arg, ctx *ArgCtx) {
+ prio, stopRecursion := arg.Type().getMutationPrio(p.Target, arg, false)
+ totalPrio += prio
+ ctx.Stop = stopRecursion
+ })
+ callPriorities = append(callPriorities, totalPrio)
+ if len(c.Args) > 0 {
+ noArgs = false
+ }
+ }
+
+ // Calls without arguments.
+ if noArgs {
+ return nil, false
+ }
+
+ return p.Calls[randomChoice(callPriorities, r)], true
+}
+
+// Generate a random index from a given 1-D array of priorities.
+func randomChoice(priorities []float64, r *randGen) int {
+ sum := float64(0)
+ probs := make([]float64, len(priorities))
+ for i, prio := range priorities {
+ sum += prio
+ probs[i] = sum
+ }
+ return sort.SearchFloat64s(probs, sum*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 {
@@ -400,49 +440,149 @@ type mutationArgs struct {
target *Target
args []Arg
ctxes []ArgCtx
+ priorities []float64
ignoreSpecial bool
}
+const (
+ maxPriority = float64(10)
+ minPriority = float64(1)
+ dontMutate = float64(0)
+)
+
func (ma *mutationArgs) collectArg(arg Arg, ctx *ArgCtx) {
ignoreSpecial := ma.ignoreSpecial
ma.ignoreSpecial = false
- switch typ := arg.Type().(type) {
- case *StructType:
- if ma.target.SpecialTypes[typ.Name()] == nil || ignoreSpecial {
- return // For structs only individual fields are updated.
- }
- // These special structs are mutated as a whole.
- ctx.Stop = true
- case *UnionType:
- if ma.target.SpecialTypes[typ.Name()] == nil && len(typ.Fields) == 1 || ignoreSpecial {
- return
- }
- ctx.Stop = true
- case *ArrayType:
- // Don't mutate fixed-size arrays.
- if typ.Kind == ArrayRangeLen && typ.RangeBegin == typ.RangeEnd {
- return
- }
- case *CsumType:
- return // Checksum is updated when the checksummed data changes.
- case *ConstType:
- return // Well, this is const.
- case *BufferType:
- if typ.Kind == BufferString && len(typ.Values) == 1 {
- return // string const
- }
- case *PtrType:
- if arg.(*PointerArg).IsSpecial() {
- // TODO: we ought to mutate this, but we don't have code for this yet.
- return
- }
- }
+
typ := arg.Type()
- if typ == nil || typ.Dir() == DirOut || !typ.Varlen() && typ.Size() == 0 {
+ prio, stopRecursion := typ.getMutationPrio(ma.target, arg, ignoreSpecial)
+ ctx.Stop = stopRecursion
+
+ if prio == dontMutate {
return
}
+
+ if typ.Dir() == DirOut || !typ.Varlen() && typ.Size() == 0 {
+ return
+ }
+
ma.args = append(ma.args, arg)
ma.ctxes = append(ma.ctxes, *ctx)
+ ma.priorities = append(ma.priorities, prio)
+}
+
+// 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.Size() * 8))) + 0.1*maxPriority
+ if t.Kind != IntRange {
+ return plainPrio, false
+ }
+
+ switch size := t.RangeEnd - t.RangeBegin + 1; {
+ case size <= 15:
+ // For a small range, we assume that it is effectively
+ // similar with FlagsType and we need to try all possible values.
+ prio = rangeSizePrio(size)
+ case size <= 256:
+ // We consider that a relevant range has at most 256
+ // values (the number of values that can be represented on a byte).
+ prio = maxPriority
+ default:
+ // Ranges larger than 256 are equivalent with a plain integer.
+ prio = plainPrio
+ }
+ return prio, false
+}
+
+func (t *StructType) getMutationPrio(target *Target, arg Arg, ignoreSpecial 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) {
+ if target.SpecialTypes[t.Name()] == nil && len(t.Fields) == 1 || ignoreSpecial {
+ return dontMutate, false
+ }
+ // For a non-special type union with more than one option
+ // we mutate the union itself and also the value of the current option.
+ if target.SpecialTypes[t.Name()] == nil {
+ return maxPriority, false
+ }
+ return maxPriority, true
+}
+
+func (t *FlagsType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) {
+ prio = rangeSizePrio(uint64(len(t.Vals)))
+ if t.BitMask {
+ // We want a higher priority because the mutation will include
+ // more possible operations (bitwise operations).
+ prio += 0.1 * maxPriority
+ }
+ return prio, false
+}
+
+// Assigns a priority based on the range size.
+func rangeSizePrio(size uint64) (prio float64) {
+ switch size {
+ case 0:
+ prio = dontMutate
+ case 1:
+ prio = minPriority
+ default:
+ // Priority proportional with the number of values. After a threshold, the priority is constant.
+ // The threshold is 15 because most of the calls have <= 15 possible values for a flag.
+ prio = math.Min(float64(size)/3+0.4*maxPriority, 0.9*maxPriority)
+ }
+ return prio
+}
+
+func (t *PtrType) getMutationPrio(target *Target, arg Arg, ignoreSpecial 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
+ }
+ return 0.3 * maxPriority, false
+}
+
+func (t *ConstType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) {
+ return dontMutate, false
+}
+
+func (t *CsumType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) {
+ return dontMutate, false
+}
+
+func (t *ProcType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) {
+ return 0.5 * maxPriority, false
+}
+
+func (t *ResourceType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) {
+ return 0.5 * maxPriority, false
+}
+
+func (t *VmaType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) {
+ return 0.5 * maxPriority, false
+}
+
+func (t *LenType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) {
+ return 0.6 * maxPriority, false
+}
+
+func (t *BufferType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) {
+ return 0.8 * maxPriority, false
+}
+
+func (t *ArrayType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) {
+ if t.Kind == ArrayRangeLen && t.RangeBegin == t.RangeEnd {
+ return dontMutate, false
+ }
+ return maxPriority, false
}
func mutateData(r *randGen, data []byte, minLen, maxLen uint64) []byte {
diff --git a/prog/mutation_test.go b/prog/mutation_test.go
index b041f02d2..9ae807e0a 100644
--- a/prog/mutation_test.go
+++ b/prog/mutation_test.go
@@ -6,6 +6,7 @@ package prog
import (
"bytes"
"fmt"
+ "math"
"math/rand"
"sync"
"testing"
@@ -22,7 +23,6 @@ func TestMutationFlags(t *testing.T) {
`r0 = mutate$flags2(&(0x7f0000000000)="2e2f66696c653000", 0x0)`,
`r0 = mutate$flags2(&(0x7f0000000000)="2e2f66696c653000", 0xd9)`,
},
-
// Mutate flags (bitmask = false).
{
`r0 = mutate$flags3(&(0x7f0000000000)="2e2f66696c653000", 0x0)`,
@@ -33,10 +33,157 @@ func TestMutationFlags(t *testing.T) {
`r0 = mutate$flags3(&(0x7f0000000000)="2e2f66696c653000", 0xaaaaaaaaaaaaaaaa)`,
},
}
+ runMutationTests(t, tests)
+}
+func TestChooseCall(t *testing.T) {
+ tests := [][2]string{
+ // The call with many arguments has a higher mutation probability.
+ {
+ `mutate0()
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)
+mutate$integer2(0x00, 0x00, 0x20, 0x00, 0x01)`,
+ `mutate0()
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0xffffffff)
+mutate$integer2(0x00, 0x00, 0x20, 0x00, 0x01)`,
+ },
+ // Calls with the same probability.
+ {
+ `mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)`,
+ `mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0xff)
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)`,
+ },
+ // The call with a lower probability can be mutated.
+ {
+ `mutate7(&(0x7f0000000000)='123', 0x3)
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)
+r0 = mutate$flags(&(0x7f0000000000)="2e2f66696c653000", 0x0, 0x1, 0x1)`,
+ `mutate7(&(0x7f0000000000)='123', 0x2)
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)
+r0 = mutate$flags(&(0x7f0000000000)="2e2f66696c653000", 0x0, 0x1, 0x1)`,
+ },
+ // Complex arguments.
+ {
+ `test$struct(&(0x7f0000000000)={0x0, {0x0}})
+test$array0(&(0x7f0000001000)={0x1, [@f0=0x2, @f1=0x3], 0x4})
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)`,
+ `test$struct(&(0x7f0000000000)={0xff, {0x0}})
+test$array0(&(0x7f0000001000)={0x1, [@f0=0x2, @f1=0x3], 0x4})
+mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)`,
+ },
+ }
runMutationTests(t, tests)
}
+func TestMutateArgument(t *testing.T) {
+ tests := [][2]string{
+ // Mutate an integer with a higher priority than the boolean arguments.
+ {
+ `mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)`,
+ `mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0xffffffff)`,
+ },
+ // Mutate a boolean.
+ {
+ `mutate$integer(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)`,
+ `mutate$integer(0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0)`,
+ },
+ // Mutate flags (bitmask = true).
+ {
+ `r0 = mutate$flags(&(0x7f0000000000)="2e2f66696c653000", 0x0, 0x1, 0x1)`,
+ `r0 = mutate$flags(&(0x7f0000000000)="2e2f66696c653000", 0x20, 0x1, 0x9)`,
+ },
+ // Mutate an int8 from a set of other arguments with higher priority.
+ {
+ `mutate$integer2(0x00, 0x00, 0x20, 0x00, 0x01)`,
+ `mutate$integer2(0x00, 0x00, 0x20, 0x00, 0x07)`,
+ },
+ // Mutate an array of structs
+ {
+ `mutate$array2(&(0x7f0000000000)=[{0x0}, {0x0}, {0x0}, {0x0}, {0x0}])`,
+ `mutate$array2(&(0x7f0000000000)=[{0x0}, {0x0}, {0x3}, {0x0}, {0x0}])`,
+ },
+ // Mutate a non-special union that have more than 1 option
+ {
+ `mutate$union(&(0x7f0000000000)=@f1=[0x0, 0x1, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x0, 0x0])`,
+ `mutate$union(&(0x7f0000000000)=@f0=0x2)`,
+ },
+ // Mutate the value of the current option in union
+ {
+ `mutate$union(&(0x7f0000000000)=@f1=[0x0, 0x1, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x0, 0x0])`,
+ `mutate$union(&(0x7f0000000000)=@f1=[0x0, 0x1, 0xff, 0x3, 0x0, 0x1, 0x2, 0x3, 0x0, 0x0])`,
+ },
+ }
+
+ target := initTargetTest(t, "test", "64")
+ for ti, test := range tests {
+ test := test
+ t.Run(fmt.Sprint(ti), func(t *testing.T) {
+ t.Parallel()
+ rs, ct, p, goal, err := buildTestContext(test, target)
+ if err != nil {
+ t.Fatalf("failed to deserialize the program: %v", err)
+ }
+ want := goal.Serialize()
+ for i := 0; i < 1e5; i++ {
+ p1 := p.Clone()
+ ctx := &mutator{
+ p: p1,
+ r: newRand(p1.Target, rs),
+ ncalls: 0,
+ ct: ct,
+ corpus: nil,
+ }
+ ctx.mutateArg()
+ data1 := p1.Serialize()
+ if bytes.Equal(want, data1) {
+ t.Logf("success on iter %v", i)
+ return
+ }
+ }
+ t.Fatalf("failed to achieve goal, original:%s\ngoal:%s", test[0], test[1])
+ })
+ }
+}
+
+func TestRandomChoice(t *testing.T) {
+ t.Parallel()
+ target, err := GetTarget("test", "64")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ r := newRand(target, randSource(t))
+ priorities := []float64{1, 1, 1, 1, 1, 1, 1, 1, 2}
+
+ const (
+ maxIters = 100000
+ searchedIdx = 8
+ prob = 0.2
+ eps = 0.01
+ )
+
+ var index, count int
+ for i := 0; i < maxIters; i++ {
+ index = randomChoice(priorities, r)
+
+ if index == searchedIdx {
+ count++
+ }
+ }
+
+ diff := math.Abs(prob*maxIters - float64(count))
+ if diff > eps*maxIters {
+ t.Fatalf("The difference (%f) is higher than %f%%", diff, eps*100)
+ }
+}
+
func TestClone(t *testing.T) {
target, rs, iters := initTest(t)
for i := 0; i < iters; i++ {
@@ -157,6 +304,12 @@ mutate5(&(0x7f0000001000)="2e2f66696c653000", 0x22c0)
mutate5(&(0x7f0000001000)="2e2f66696c653000", 0x22c0)
mutate5(&(0x7f0000001000)="2e2f66696c653100", 0x22c0)
`},
+ // Mutate the array.
+ {`
+mutate$array(0x1, 0x30, &(0x7f0000000000)=[0x1, 0x1, 0x1, 0x1, 0x1])
+`, `
+mutate$array(0x1, 0x30, &(0x7f0000000000)=[0x1, 0x1, 0x1, 0x1])
+`},
// Extend an array.
{`
mutate3(&(0x7f0000000000)=[0x1, 0x1], 0x2)
@@ -180,45 +333,6 @@ mutate8(0xffffffffffffffff)
runMutationTests(t, tests)
}
-func runMutationTests(t *testing.T, tests [][2]string) {
- target := initTargetTest(t, "test", "64")
-
- for ti, test := range tests {
- test := test
- t.Run(fmt.Sprint(ti), func(t *testing.T) {
- t.Parallel()
- p, err := target.Deserialize([]byte(test[0]), Strict)
- if err != nil {
- t.Fatalf("failed to deserialize original program: %v", err)
- }
- goal, err := target.Deserialize([]byte(test[1]), Strict)
- if err != nil {
- t.Fatalf("failed to deserialize goal program: %v", err)
- }
- want := goal.Serialize()
- enabled := make(map[*Syscall]bool)
- for _, c := range p.Calls {
- enabled[c.Meta] = true
- }
- for _, c := range goal.Calls {
- enabled[c.Meta] = true
- }
- ct := target.BuildChoiceTable(nil, enabled)
- rs := rand.NewSource(0)
- for i := 0; i < 1e5; i++ {
- p1 := p.Clone()
- p1.Mutate(rs, len(goal.Calls), ct, nil)
- data1 := p1.Serialize()
- if bytes.Equal(want, data1) {
- t.Logf("success on iter %v", i)
- return
- }
- }
- t.Fatalf("failed to achieve goal, original:%s\ngoal:%s", test[0], test[1])
- })
- }
-}
-
func BenchmarkMutate(b *testing.B) {
target, cleanup := initBench(b)
defer cleanup()
@@ -259,3 +373,49 @@ func linuxAmd64ChoiceTable(target *Target) *ChoiceTable {
})
return linuxCT
}
+
+func runMutationTests(t *testing.T, tests [][2]string) {
+ target := initTargetTest(t, "test", "64")
+ for ti, test := range tests {
+ test := test
+ t.Run(fmt.Sprint(ti), func(t *testing.T) {
+ t.Parallel()
+ rs, ct, p, goal, err := buildTestContext(test, target)
+ if err != nil {
+ t.Fatalf("failed to deserialize the program: %v", err)
+ }
+ want := goal.Serialize()
+ for i := 0; i < 1e5; i++ {
+ p1 := p.Clone()
+ p1.Mutate(rs, len(goal.Calls), ct, nil)
+ data1 := p1.Serialize()
+ if bytes.Equal(want, data1) {
+ t.Logf("success on iter %v", i)
+ return
+ }
+ }
+ t.Fatalf("failed to achieve goal, original:%s\ngoal:%s", test[0], test[1])
+ })
+ }
+}
+
+func buildTestContext(test [2]string, target *Target) (rs rand.Source, ct *ChoiceTable, p, goal *Prog, err error) {
+ p, err = target.Deserialize([]byte(test[0]), Strict)
+ if err != nil {
+ return
+ }
+ goal, err = target.Deserialize([]byte(test[1]), Strict)
+ if err != nil {
+ return
+ }
+ enabled := make(map[*Syscall]bool)
+ for _, c := range p.Calls {
+ enabled[c.Meta] = true
+ }
+ for _, c := range goal.Calls {
+ enabled[c.Meta] = true
+ }
+ ct = target.BuildChoiceTable(nil, enabled)
+ rs = rand.NewSource(0)
+ return
+}
diff --git a/prog/types.go b/prog/types.go
index c36dc076c..9a58a9eee 100644
--- a/prog/types.go
+++ b/prog/types.go
@@ -65,6 +65,7 @@ type Type interface {
isDefaultArg(arg Arg) bool
generate(r *randGen, s *state) (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)
minimize(ctx *minimizeArgsCtx, arg Arg, path string) bool
}
diff --git a/sys/test/gen/64.go b/sys/test/gen/64.go
index 0d8c4af87..f0c1afaea 100644
--- a/sys/test/gen/64.go
+++ b/sys/test/gen/64.go
@@ -646,6 +646,9 @@ var syscalls_64 = []*Syscall{
&IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int64", FldName: "i2", TypeSize: 8}}, Kind: 2, RangeEnd: 536870911},
&PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "vec", TypeSize: 8}, Type: &ArrayType{TypeCommon: TypeCommon{TypeName: "array", IsVarlen: true}, Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}, Kind: 2, RangeEnd: 1}}},
}},
+ {Name: "mutate$array2", CallName: "mutate", MissingArgs: 8, Args: []Type{
+ &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "vec", TypeSize: 8}, Type: &ArrayType{TypeCommon: TypeCommon{TypeName: "array", IsVarlen: true}, Type: &StructType{Key: StructKey{Name: "syz_struct1"}}}},
+ }},
{Name: "mutate$flags", CallName: "mutate", MissingArgs: 5, Args: []Type{
&PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "filename", TypeSize: 8}, Type: &BufferType{TypeCommon: TypeCommon{TypeName: "filename", IsVarlen: true}, Kind: 3}},
&IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int64", FldName: "i1", TypeSize: 8}}},
@@ -678,6 +681,9 @@ var syscalls_64 = []*Syscall{
&IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int16", FldName: "i3", TypeSize: 2}}, Kind: 2, RangeEnd: 8},
&IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int8", FldName: "i4", TypeSize: 1}}, Kind: 2, RangeEnd: 8},
}},
+ {Name: "mutate$union", CallName: "mutate", MissingArgs: 8, Args: []Type{
+ &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "p", TypeSize: 8}, Type: &UnionType{Key: StructKey{Name: "syz_union0"}}},
+ }},
{Name: "mutate0", CallName: "mutate0"},
{Name: "mutate1", CallName: "mutate1"},
{Name: "mutate2", CallName: "mutate2"},
@@ -1077,4 +1083,4 @@ var consts_64 = []ConstValue{
{Name: "SYS_unsupported"},
}
-const revision_64 = "aaee55a3532998a140b25dc52cc113c4de048dd2"
+const revision_64 = "71d8f9a8f2ed06129920bb9f60aef16fb9ca749b"
diff --git a/sys/test/test.txt b/sys/test/test.txt
index 1788e5ba0..bb2b4547a 100644
--- a/sys/test/test.txt
+++ b/sys/test/test.txt
@@ -741,6 +741,8 @@ mutate$flags(filename ptr[in, filename], i1 int64, b1 bool16, flags flags[bitmas
mutate$flags2(filename ptr[in, filename], flags flags[bitmask_flags2])
mutate$flags3(filename ptr[in, filename], flags flags[open_flags2])
mutate$array(i1 int64, i2 int64[0x0:0x1fffffff], vec ptr[in, array[int32[0:1]]])
+mutate$array2(vec ptr[in, array[syz_struct1]])
+mutate$union(p ptr[in, syz_union0])
open_flags = 0xabababababababab, 0xcdcdcdcdcdcdcdcd
open_flags2 = 0xaaaaaaaaaaaaaaaa, 0xaaaaaaaabbbbbbbb, 0xbbbbbbbbbbbbbbbb, 0xbbbbbbbbcccccccc, 0xcccccccccccccccc, 0xccccccccdddddddd, 0xdddddddddddddddd, 0xddddddddeeeeeeee, 0xeeeeeeeeeeeeeeee, 0xeeeeeeeeffffffff, 0xffffffffffffffff