prog: mutate length of output buffers

Update #480
diff --git a/executor/defs.h b/executor/defs.h
index 1c615d8..a80b5cd 100644
--- a/executor/defs.h
+++ b/executor/defs.h
@@ -175,7 +175,7 @@
 
 #if GOARCH_64
 #define GOARCH "64"
-#define SYZ_REVISION "71d8f9a8f2ed06129920bb9f60aef16fb9ca749b"
+#define SYZ_REVISION "d3a892553e6e706b3c4f1a086bdd22a2578120c1"
 #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 946283f..57cbc46 100644
--- a/executor/syscalls.h
+++ b/executor/syscalls.h
@@ -16503,11 +16503,13 @@
     {"minimize$0", 0},
     {"mutate$array", 0},
     {"mutate$array2", 0},
+    {"mutate$buffer", 0},
     {"mutate$flags", 0},
     {"mutate$flags2", 0},
     {"mutate$flags3", 0},
     {"mutate$integer", 0},
     {"mutate$integer2", 0},
+    {"mutate$rangedbuffer", 0},
     {"mutate$union", 0},
     {"mutate0", 0},
     {"mutate1", 0},
diff --git a/prog/mutation.go b/prog/mutation.go
index 8e59880..3f5129f 100644
--- a/prog/mutation.go
+++ b/prog/mutation.go
@@ -306,19 +306,22 @@
 }
 
 func (t *BufferType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {
+	minLen, maxLen := uint64(0), maxBlobLen
+	if t.Kind == BufferBlobRange {
+		minLen, maxLen = t.RangeBegin, t.RangeEnd
+	}
 	a := arg.(*DataArg)
+	if t.Dir() == DirOut {
+		mutateBufferSize(r, a, minLen, maxLen)
+		return
+	}
 	switch t.Kind {
 	case BufferBlobRand, BufferBlobRange:
 		data := append([]byte{}, a.Data()...)
-		minLen, maxLen := uint64(0), maxBlobLen
-		if t.Kind == BufferBlobRange {
-			minLen, maxLen = t.RangeBegin, t.RangeEnd
-		}
 		a.data = mutateData(r, data, minLen, maxLen)
 	case BufferString:
 		data := append([]byte{}, a.Data()...)
 		if r.bin() {
-			minLen, maxLen := uint64(0), maxBlobLen
 			if t.TypeSize != 0 {
 				minLen, maxLen = t.TypeSize, t.TypeSize
 			}
@@ -337,6 +340,18 @@
 	return
 }
 
+func mutateBufferSize(r *randGen, arg *DataArg, minLen, maxLen uint64) {
+	for oldSize := arg.Size(); oldSize == arg.Size(); {
+		arg.size += uint64(r.Intn(33)) - 16
+		if arg.size < minLen {
+			arg.size = minLen
+		}
+		if arg.size > 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)
@@ -464,7 +479,9 @@
 		return
 	}
 
-	if typ.Dir() == DirOut || !typ.Varlen() && typ.Size() == 0 {
+	_, isArrayTyp := typ.(*ArrayType)
+	_, isBufferTyp := typ.(*BufferType)
+	if !isBufferTyp && !isArrayTyp && typ.Dir() == DirOut || !typ.Varlen() && typ.Size() == 0 {
 		return
 	}
 
@@ -577,6 +594,9 @@
 }
 
 func (t *BufferType) getMutationPrio(target *Target, arg Arg, ignoreSpecial bool) (prio float64, stopRecursion bool) {
+	if t.Dir() == DirOut && !t.Varlen() {
+		return dontMutate, false
+	}
 	return 0.8 * maxPriority, false
 }
 
diff --git a/prog/mutation_test.go b/prog/mutation_test.go
index 6d19300..76812df 100644
--- a/prog/mutation_test.go
+++ b/prog/mutation_test.go
@@ -33,7 +33,7 @@
 			`r0 = mutate$flags3(&(0x7f0000000000)="2e2f66696c653000", 0xaaaaaaaaaaaaaaaa)`,
 		},
 	}
-	runMutationTests(t, tests)
+	runMutationTests(t, tests, true)
 }
 
 func TestChooseCall(t *testing.T) {
@@ -79,7 +79,7 @@
 mutate$integer(0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1)`,
 		},
 	}
-	runMutationTests(t, tests)
+	runMutationTests(t, tests, true)
 }
 
 func TestMutateArgument(t *testing.T) {
@@ -359,9 +359,44 @@
 `, `
 mutate8(0xffffffffffffffff)
 `},
+		// Increase buffer length
+		{`
+mutate$buffer(&(0x7f0000000000)=""/100)
+`, `
+mutate$buffer(&(0x7f0000000000)=""/200)
+`},
+		// Decrease buffer length
+		{`
+mutate$buffer(&(0x7f0000000000)=""/800)
+`, `
+mutate$buffer(&(0x7f0000000000)=""/4)
+`},
+		// Mutate a ranged buffer
+		{`
+mutate$rangedbuffer(&(0x7f00000000c0)=""/10)
+`, `
+mutate$rangedbuffer(&(0x7f00000000c0)=""/7)
+`},
 	}
 
-	runMutationTests(t, tests)
+	runMutationTests(t, tests, true)
+}
+
+func TestNegativeMutations(t *testing.T) {
+	tests := [][2]string{
+		// Mutate buffer size outside the range limits
+		{`
+mutate$rangedbuffer(&(0x7f00000000c0)=""/7)
+`, `
+mutate$rangedbuffer(&(0x7f00000000c0)=""/4)
+`},
+		{`
+mutate$rangedbuffer(&(0x7f00000000c0)=""/7)
+`, `
+mutate$rangedbuffer(&(0x7f00000000c0)=""/11)
+`},
+	}
+	runMutationTests(t, tests, false)
 }
 
 func BenchmarkMutate(b *testing.B) {
@@ -405,7 +440,7 @@
 	return linuxCT
 }
 
-func runMutationTests(t *testing.T, tests [][2]string) {
+func runMutationTests(t *testing.T, tests [][2]string, valid bool) {
 	target := initTargetTest(t, "test", "64")
 	for ti, test := range tests {
 		test := test
@@ -416,16 +451,25 @@
 				t.Fatalf("failed to deserialize the program: %v", err)
 			}
 			want := goal.Serialize()
-			for i := 0; i < 1e5; i++ {
+			iters := int(1e5)
+			if !valid {
+				iters /= 10
+			}
+			for i := 0; i < iters; i++ {
 				p1 := p.Clone()
 				p1.Mutate(rs, len(goal.Calls), ct, nil)
 				data1 := p1.Serialize()
 				if bytes.Equal(want, data1) {
+					if !valid {
+						t.Fatalf("failed on iter %v", i)
+					}
 					t.Logf("success on iter %v", i)
 					return
 				}
 			}
-			t.Fatalf("failed to achieve goal, original:%s\ngoal:%s", test[0], test[1])
+			if valid {
+				t.Fatalf("failed to achieve goal, original:%s\ngoal:%s", test[0], test[1])
+			}
 		})
 	}
 }
diff --git a/sys/test/gen/64.go b/sys/test/gen/64.go
index f0c1afa..886f8d3 100644
--- a/sys/test/gen/64.go
+++ b/sys/test/gen/64.go
@@ -649,6 +649,9 @@
 	{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$buffer", CallName: "mutate", MissingArgs: 8, Args: []Type{
+		&PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "p", TypeSize: 8}, Type: &BufferType{TypeCommon: TypeCommon{TypeName: "array", ArgDir: 1, IsVarlen: true}}},
+	}},
 	{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}}},
@@ -681,6 +684,9 @@
 		&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$rangedbuffer", CallName: "mutate", MissingArgs: 8, Args: []Type{
+		&PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "p", TypeSize: 8}, Type: &BufferType{TypeCommon: TypeCommon{TypeName: "array", ArgDir: 1, IsVarlen: true}, Kind: 1, RangeBegin: 5, RangeEnd: 10}},
+	}},
 	{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"}}},
 	}},
@@ -1083,4 +1089,4 @@
 	{Name: "SYS_unsupported"},
 }
 
-const revision_64 = "71d8f9a8f2ed06129920bb9f60aef16fb9ca749b"
+const revision_64 = "d3a892553e6e706b3c4f1a086bdd22a2578120c1"
diff --git a/sys/test/test.txt b/sys/test/test.txt
index bb2b454..e071ad3 100644
--- a/sys/test/test.txt
+++ b/sys/test/test.txt
@@ -743,6 +743,8 @@
 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])
+mutate$buffer(p buffer[out])
+mutate$rangedbuffer(p ptr[out, array[int8, 5:10]])
 
 open_flags = 0xabababababababab, 0xcdcdcdcdcdcdcdcd
 open_flags2 = 0xaaaaaaaaaaaaaaaa, 0xaaaaaaaabbbbbbbb, 0xbbbbbbbbbbbbbbbb, 0xbbbbbbbbcccccccc, 0xcccccccccccccccc, 0xccccccccdddddddd, 0xdddddddddddddddd, 0xddddddddeeeeeeee, 0xeeeeeeeeeeeeeeee, 0xeeeeeeeeffffffff, 0xffffffffffffffff