aboutsummaryrefslogtreecommitdiffstats
path: root/prog
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2021-03-04 16:03:50 +0100
committerDmitry Vyukov <dvyukov@google.com>2021-03-04 19:44:53 +0100
commitb2bebe1217cea83046897e28cf1366b72c3ff329 (patch)
treec38c78e25e5eb162c0900a075e3ed42d8ac54069 /prog
parentc28569d10158d746caf5eb46e6000cc686af96c7 (diff)
prog: detect copyout overflow
Detect the case when a program requires more copyout than executor can handle. Curretnly these result in: "SYZFAIL: command refers to bad result" failures. Now syz-fuzzer should ignore them.
Diffstat (limited to 'prog')
-rw-r--r--prog/encodingexec.go4
-rw-r--r--prog/encodingexec_test.go67
2 files changed, 70 insertions, 1 deletions
diff --git a/prog/encodingexec.go b/prog/encodingexec.go
index c15eb25bc..e0c7ec59d 100644
--- a/prog/encodingexec.go
+++ b/prog/encodingexec.go
@@ -52,6 +52,8 @@ const (
const (
ExecBufferSize = 4 << 20 // keep in sync with kMaxInput in executor.cc
ExecNoCopyout = ^uint64(0)
+
+ execMaxCommands = 1000 // executor knows about this constant (kMaxCommands)
)
var ErrExecBufferTooSmall = errors.New("encodingexec: provided buffer is too small")
@@ -72,7 +74,7 @@ func (p *Prog) SerializeForExec(buffer []byte) (int, error) {
w.serializeCall(c)
}
w.write(execInstrEOF)
- if w.eof {
+ if w.eof || w.copyoutSeq > execMaxCommands {
return 0, ErrExecBufferTooSmall
}
return len(buffer) - len(w.buf), nil
diff --git a/prog/encodingexec_test.go b/prog/encodingexec_test.go
index 7cda63cc0..d5b92f59a 100644
--- a/prog/encodingexec_test.go
+++ b/prog/encodingexec_test.go
@@ -498,3 +498,70 @@ func TestSerializeForExec(t *testing.T) {
})
}
}
+
+func TestSerializeForExecOverflow(t *testing.T) {
+ target := initTargetTest(t, "test", "64")
+ type Test struct {
+ name string
+ overflow bool
+ gen func(w *bytes.Buffer)
+ }
+ tests := []Test{
+ {
+ name: "few-resources",
+ overflow: false,
+ gen: func(w *bytes.Buffer) {
+ for i := 0; i < execMaxCommands-10; i++ {
+ fmt.Fprintf(w, "r%v = test$res0()\ntest$res1(r%v)\n", i, i)
+ }
+ },
+ },
+ {
+ name: "overflow-resources",
+ overflow: true,
+ gen: func(w *bytes.Buffer) {
+ for i := 0; i < execMaxCommands+1; i++ {
+ fmt.Fprintf(w, "r%v = test$res0()\ntest$res1(r%v)\n", i, i)
+ }
+ },
+ },
+ {
+ name: "no-verflow-buffer",
+ overflow: false,
+ gen: func(w *bytes.Buffer) {
+ fmt.Fprintf(w, "r0 = test$res0()\n")
+ for i := 0; i < 58e3; i++ {
+ fmt.Fprintf(w, "test$res1(r0)\n")
+ }
+ },
+ },
+ {
+ name: "overflow-buffer",
+ overflow: true,
+ gen: func(w *bytes.Buffer) {
+ fmt.Fprintf(w, "r0 = test$res0()\n")
+ for i := 0; i < 59e3; i++ {
+ fmt.Fprintf(w, "test$res1(r0)\n")
+ }
+ },
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ data := new(bytes.Buffer)
+ test.gen(data)
+ p, err := target.Deserialize(data.Bytes(), Strict)
+ if err != nil {
+ t.Fatal(err)
+ }
+ buf := make([]byte, ExecBufferSize)
+ _, err = p.SerializeForExec(buf)
+ if test.overflow && err != ErrExecBufferTooSmall {
+ t.Fatalf("want overflow but got %v", err)
+ }
+ if !test.overflow && err != nil {
+ t.Fatalf("want no overflow but got %v", err)
+ }
+ })
+ }
+}