]> Cypherpunks repositories - gostls13.git/commitdiff
bufio: permit r.Reset(r) without infinite recursion
authorIan Lance Taylor <iant@golang.org>
Thu, 9 Feb 2023 03:50:06 +0000 (19:50 -0800)
committerGopher Robot <gobot@golang.org>
Fri, 10 Feb 2023 18:56:01 +0000 (18:56 +0000)
This can happen in reasonable code because NewReader(r) can return r,
if r is already a Reader.

Similarly for Writer.

Fixes #58423

Change-Id: Iff9d9265410bee68fbaeb7175369847bd737eb2c
Reviewed-on: https://go-review.googlesource.com/c/go/+/466815
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>

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

index 5a88def0c75b5e7d5d2fe8f2d3b521519f018f7a..8469b9eff75100c42b6ad0eaef4a401623df1eb5 100644 (file)
@@ -70,7 +70,14 @@ func (b *Reader) Size() int { return len(b.buf) }
 // the buffered reader to read from r.
 // Calling Reset on the zero value of Reader initializes the internal buffer
 // to the default size.
+// Calling b.Reset(b) (that is, resetting a Reader to itself) does nothing.
 func (b *Reader) Reset(r io.Reader) {
+       // If a Reader r is passed to NewReader, NewReader will return r.
+       // Different layers of code may do that, and then later pass r
+       // to Reset. Avoid infinite recursion in that case.
+       if b == r {
+               return
+       }
        if b.buf == nil {
                b.buf = make([]byte, defaultBufSize)
        }
@@ -608,7 +615,14 @@ func (b *Writer) Size() int { return len(b.buf) }
 // resets b to write its output to w.
 // Calling Reset on the zero value of Writer initializes the internal buffer
 // to the default size.
+// Calling w.Reset(w) (that is, resetting a Writer to itself) does nothing.
 func (b *Writer) Reset(w io.Writer) {
+       // If a Writer w is passed to NewWriter, NewWriter will return w.
+       // Different layers of code may do that, and then later pass w
+       // to Reset. Avoid infinite recursion in that case.
+       if b == w {
+               return
+       }
        if b.buf == nil {
                b.buf = make([]byte, defaultBufSize)
        }
index 64ccd025ea11a9ff8aaffa633f068a930836eccd..a8c1e503978847c3473bf964349959397b0cf552 100644 (file)
@@ -1482,6 +1482,17 @@ func TestReadZero(t *testing.T) {
 }
 
 func TestReaderReset(t *testing.T) {
+       checkAll := func(r *Reader, want string) {
+               t.Helper()
+               all, err := io.ReadAll(r)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if string(all) != want {
+                       t.Errorf("ReadAll returned %q, want %q", all, want)
+               }
+       }
+
        r := NewReader(strings.NewReader("foo foo"))
        buf := make([]byte, 3)
        r.Read(buf)
@@ -1490,27 +1501,23 @@ func TestReaderReset(t *testing.T) {
        }
 
        r.Reset(strings.NewReader("bar bar"))
-       all, err := io.ReadAll(r)
-       if err != nil {
-               t.Fatal(err)
-       }
-       if string(all) != "bar bar" {
-               t.Errorf("ReadAll = %q; want bar bar", all)
-       }
+       checkAll(r, "bar bar")
 
        *r = Reader{} // zero out the Reader
        r.Reset(strings.NewReader("bar bar"))
-       all, err = io.ReadAll(r)
-       if err != nil {
-               t.Fatal(err)
-       }
-       if string(all) != "bar bar" {
-               t.Errorf("ReadAll = %q; want bar bar", all)
-       }
+       checkAll(r, "bar bar")
+
+       // Wrap a reader and then Reset to that reader.
+       r.Reset(strings.NewReader("recur"))
+       r2 := NewReader(r)
+       checkAll(r2, "recur")
+       r.Reset(strings.NewReader("recur2"))
+       r2.Reset(r)
+       checkAll(r2, "recur2")
 }
 
 func TestWriterReset(t *testing.T) {
-       var buf1, buf2, buf3 strings.Builder
+       var buf1, buf2, buf3, buf4, buf5 strings.Builder
        w := NewWriter(&buf1)
        w.WriteString("foo")
 
@@ -1534,6 +1541,22 @@ func TestWriterReset(t *testing.T) {
        if buf3.String() != "bar" {
                t.Errorf("buf3 = %q; want bar", buf3.String())
        }
+
+       // Wrap a writer and then Reset to that writer.
+       w.Reset(&buf4)
+       w2 := NewWriter(w)
+       w2.WriteString("recur")
+       w2.Flush()
+       if buf4.String() != "recur" {
+               t.Errorf("buf4 = %q, want %q", buf4.String(), "recur")
+       }
+       w.Reset(&buf5)
+       w2.Reset(w)
+       w2.WriteString("recur2")
+       w2.Flush()
+       if buf5.String() != "recur2" {
+               t.Errorf("buf5 = %q, want %q", buf5.String(), "recur2")
+       }
 }
 
 func TestReaderDiscard(t *testing.T) {