]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.17] compress/gzip: fix stack exhaustion bug in Reader.Read
authorTatiana Bradley <tatiana@golang.org>
Fri, 6 May 2022 15:25:06 +0000 (11:25 -0400)
committerMichael Knyszek <mknyszek@google.com>
Tue, 12 Jul 2022 15:20:33 +0000 (15:20 +0000)
Replace recursion with iteration in Reader.Read to avoid stack
exhaustion when there are a large number of files.

Fixes CVE-2022-30631
Fixes #53717
Updates #53168

Change-Id: I47d8afe3f2d40b0213ab61431df9b221794dbfe0
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1455673
Reviewed-by: Roland Shoemaker <bracewell@google.com>
Reviewed-by: Julie Qiu <julieqiu@google.com>
(cherry picked from commit cf498969c8a0bae9d7a24b98fc1f66c824a4775d)
Reviewed-on: https://go-review.googlesource.com/c/go/+/417071
Reviewed-by: Heschi Kreinick <heschi@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/compress/gzip/gunzip.go
src/compress/gzip/gunzip_test.go

index 924bce10b7c0cb1a901f47255afea99efcfc298a..237b2b928bfed0cd3223982e94b7afda212614cb 100644 (file)
@@ -248,42 +248,40 @@ func (z *Reader) Read(p []byte) (n int, err error) {
                return 0, z.err
        }
 
-       n, z.err = z.decompressor.Read(p)
-       z.digest = crc32.Update(z.digest, crc32.IEEETable, p[:n])
-       z.size += uint32(n)
-       if z.err != io.EOF {
-               // In the normal case we return here.
-               return n, z.err
-       }
+       for n == 0 {
+               n, z.err = z.decompressor.Read(p)
+               z.digest = crc32.Update(z.digest, crc32.IEEETable, p[:n])
+               z.size += uint32(n)
+               if z.err != io.EOF {
+                       // In the normal case we return here.
+                       return n, z.err
+               }
 
-       // Finished file; check checksum and size.
-       if _, err := io.ReadFull(z.r, z.buf[:8]); err != nil {
-               z.err = noEOF(err)
-               return n, z.err
-       }
-       digest := le.Uint32(z.buf[:4])
-       size := le.Uint32(z.buf[4:8])
-       if digest != z.digest || size != z.size {
-               z.err = ErrChecksum
-               return n, z.err
-       }
-       z.digest, z.size = 0, 0
+               // Finished file; check checksum and size.
+               if _, err := io.ReadFull(z.r, z.buf[:8]); err != nil {
+                       z.err = noEOF(err)
+                       return n, z.err
+               }
+               digest := le.Uint32(z.buf[:4])
+               size := le.Uint32(z.buf[4:8])
+               if digest != z.digest || size != z.size {
+                       z.err = ErrChecksum
+                       return n, z.err
+               }
+               z.digest, z.size = 0, 0
 
-       // File is ok; check if there is another.
-       if !z.multistream {
-               return n, io.EOF
-       }
-       z.err = nil // Remove io.EOF
+               // File is ok; check if there is another.
+               if !z.multistream {
+                       return n, io.EOF
+               }
+               z.err = nil // Remove io.EOF
 
-       if _, z.err = z.readHeader(); z.err != nil {
-               return n, z.err
+               if _, z.err = z.readHeader(); z.err != nil {
+                       return n, z.err
+               }
        }
 
-       // Read from next file, if necessary.
-       if n > 0 {
-               return n, nil
-       }
-       return z.Read(p)
+       return n, nil
 }
 
 // Close closes the Reader. It does not close the underlying io.Reader.
index 17c23e8a9be8585e386c97442c07533e3d2677a9..6fe8ddcf558eb6c4a671032dbb44069bdfde941f 100644 (file)
@@ -515,3 +515,19 @@ func TestTruncatedStreams(t *testing.T) {
                }
        }
 }
+
+func TestCVE202230631(t *testing.T) {
+       var empty = []byte{0x1f, 0x8b, 0x08, 0x00, 0xa7, 0x8f, 0x43, 0x62, 0x00,
+               0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+       r := bytes.NewReader(bytes.Repeat(empty, 4e6))
+       z, err := NewReader(r)
+       if err != nil {
+               t.Fatalf("NewReader: got %v, want nil", err)
+       }
+       // Prior to CVE-2022-30631 fix, this would cause an unrecoverable panic due
+       // to stack exhaustion.
+       _, err = z.Read(make([]byte, 10))
+       if err != io.EOF {
+               t.Errorf("Reader.Read: got %v, want %v", err, io.EOF)
+       }
+}