]> Cypherpunks repositories - gostls13.git/commitdiff
bufio: fix potential endless loop in ReadByte
authorRobert Griesemer <gri@golang.org>
Thu, 10 Apr 2014 00:53:09 +0000 (17:53 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 10 Apr 2014 00:53:09 +0000 (17:53 -0700)
Fixes #7745.

LGTM=bradfitz, r
R=r, bradfitz
CC=golang-codereviews
https://golang.org/cl/86220044

src/pkg/bufio/bufio.go
src/pkg/bufio/bufio_test.go

index 1e0cdae38e8ebdb862d9a46c6805a9684fc2e356..2f51badc9c894ac9bc067107d3109466d58086ff 100644 (file)
@@ -88,15 +88,22 @@ func (b *Reader) fill() {
                b.r = 0
        }
 
-       // Read new data.
-       n, err := b.rd.Read(b.buf[b.w:])
-       if n < 0 {
-               panic(errNegativeRead)
-       }
-       b.w += n
-       if err != nil {
-               b.err = err
+       // Read new data: try a limited number of times.
+       for i := maxConsecutiveEmptyReads; i > 0; i-- {
+               n, err := b.rd.Read(b.buf[b.w:])
+               if n < 0 {
+                       panic(errNegativeRead)
+               }
+               b.w += n
+               if err != nil {
+                       b.err = err
+                       return
+               }
+               if n > 0 {
+                       return
+               }
        }
+       b.err = io.ErrNoProgress
 }
 
 func (b *Reader) readErr() error {
@@ -151,6 +158,9 @@ func (b *Reader) Read(p []byte) (n int, err error) {
                        // Large read, empty buffer.
                        // Read directly into p to avoid copy.
                        n, b.err = b.rd.Read(p)
+                       if n < 0 {
+                               panic(errNegativeRead)
+                       }
                        if n > 0 {
                                b.lastByte = int(p[n-1])
                                b.lastRuneSize = -1
index 32ca86161fc0f087f16edd9c0a8d47296c5cba1a..406eb153ba11ad08f3ddac3b86fed151e47da644 100644 (file)
@@ -14,6 +14,7 @@ import (
        "strings"
        "testing"
        "testing/iotest"
+       "time"
        "unicode/utf8"
 )
 
@@ -174,6 +175,34 @@ func TestReader(t *testing.T) {
        }
 }
 
+type zeroReader struct{}
+
+func (zeroReader) Read(p []byte) (int, error) {
+       return 0, nil
+}
+
+func TestZeroReader(t *testing.T) {
+       var z zeroReader
+       r := NewReader(z)
+
+       c := make(chan error)
+       go func() {
+               _, err := r.ReadByte()
+               c <- err
+       }()
+
+       select {
+       case err := <-c:
+               if err == nil {
+                       t.Error("error expected")
+               } else if err != io.ErrNoProgress {
+                       t.Error("unexpected error:", err)
+               }
+       case <-time.After(time.Second):
+               t.Error("test timed out (endless loop in ReadByte?)")
+       }
+}
+
 // A StringReader delivers its data one string segment at a time via Read.
 type StringReader struct {
        data []string