From 4a13f6f42ded127dc0d097f7d1f89916cbd9e729 Mon Sep 17 00:00:00 2001 From: Phil Bracikowski Date: Wed, 2 Mar 2022 01:51:28 +0000 Subject: [PATCH] compress/gzip: return unexpected EOF for certain truncated streams For cases where RFC 1952 requires a field, the code returns the error io.ErrUnexpectedEOF except in two places: for the FNAME flag or the FCOMMENT flag. These flags expect a null-terminated string and readString may return an EOF if the Reader is truncated before a null byte is found. For consistency with parsing other parts of the header, this is converted to an unexpected EOF herein. Follow-up to CL 14832. Fixes #51417 Change-Id: I173283a6ae309e4a8e52fc15df404ce5db06eff1 GitHub-Last-Rev: 2e573cd961c0b1e4296fbde53097cf5723a2ca29 GitHub-Pull-Request: golang/go#51418 Reviewed-on: https://go-review.googlesource.com/c/go/+/389034 Reviewed-by: Joseph Tsai Trust: Joseph Tsai Trust: Ian Lance Taylor --- src/compress/gzip/gunzip.go | 4 +- src/compress/gzip/gunzip_test.go | 80 ++++++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/compress/gzip/gunzip.go b/src/compress/gzip/gunzip.go index 924bce10b7..aa6780f847 100644 --- a/src/compress/gzip/gunzip.go +++ b/src/compress/gzip/gunzip.go @@ -211,14 +211,14 @@ func (z *Reader) readHeader() (hdr Header, err error) { var s string if flg&flagName != 0 { if s, err = z.readString(); err != nil { - return hdr, err + return hdr, noEOF(err) } hdr.Name = s } if flg&flagComment != 0 { if s, err = z.readString(); err != nil { - return hdr, err + return hdr, noEOF(err) } hdr.Comment = s } diff --git a/src/compress/gzip/gunzip_test.go b/src/compress/gzip/gunzip_test.go index 17c23e8a9b..be69185463 100644 --- a/src/compress/gzip/gunzip_test.go +++ b/src/compress/gzip/gunzip_test.go @@ -359,6 +359,38 @@ var gunzipTests = []gunzipTest{ }, io.ErrUnexpectedEOF, }, + { + "hello.txt", + "gzip header with truncated name", + "hello world\n", + []byte{ + 0x1f, 0x8b, 0x08, 0x08, 0xc8, 0x58, 0x13, 0x4a, + 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, + 0x74, 0x78, 0x74, 0x00, 0xcb, 0x48, 0xcd, 0xc9, + 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0xe1, + 0x02, 0x00, 0x2d, 0x3b, 0x08, 0xaf, 0x0c, 0x00, + 0x00, 0x00, + 0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x01, + }, + io.ErrUnexpectedEOF, + }, + { + "", + "gzip header with truncated comment", + "hello world\n", + []byte{ + 0x1f, 0x8b, 0x08, 0x10, 0xc8, 0x58, 0x13, 0x4a, + 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, + 0x74, 0x78, 0x74, 0x00, 0xcb, 0x48, 0xcd, 0xc9, + 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0xe1, + 0x02, 0x00, 0x2d, 0x3b, 0x08, 0xaf, 0x0c, 0x00, + 0x00, 0x00, + 0x1f, 0x8b, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x01, + }, + io.ErrUnexpectedEOF, + }, } func TestDecompressor(t *testing.T) { @@ -495,23 +527,45 @@ func TestNilStream(t *testing.T) { } func TestTruncatedStreams(t *testing.T) { - const data = "\x1f\x8b\b\x04\x00\tn\x88\x00\xff\a\x00foo bar\xcbH\xcd\xc9\xc9\xd7Q(\xcf/\xcaI\x01\x04:r\xab\xff\f\x00\x00\x00" + cases := []struct { + name string + data []byte + }{ + { + name: "original", + data: []byte("\x1f\x8b\b\x04\x00\tn\x88\x00\xff\a\x00foo bar\xcbH\xcd\xc9\xc9\xd7Q(\xcf/\xcaI\x01\x04:r\xab\xff\f\x00\x00\x00"), + }, + { + name: "truncated name", + data: []byte{ + 0x1f, 0x8b, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, + }, + }, + { + name: "truncated comment", + data: []byte{ + 0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, + }, + }, + } // Intentionally iterate starting with at least one byte in the stream. - for i := 1; i < len(data)-1; i++ { - r, err := NewReader(strings.NewReader(data[:i])) - if err != nil { + for _, tc := range cases { + for i := 1; i < len(tc.data); i++ { + r, err := NewReader(strings.NewReader(string(tc.data[:i]))) + if err != nil { + if err != io.ErrUnexpectedEOF { + t.Errorf("NewReader(%s-%d) on truncated stream: got %v, want %v", tc.name, i, err, io.ErrUnexpectedEOF) + } + continue + } + _, err = io.Copy(io.Discard, r) + if ferr, ok := err.(*flate.ReadError); ok { + err = ferr.Err + } if err != io.ErrUnexpectedEOF { - t.Errorf("NewReader(%d) on truncated stream: got %v, want %v", i, err, io.ErrUnexpectedEOF) + t.Errorf("io.Copy(%s-%d) on truncated stream: got %v, want %v", tc.name, i, err, io.ErrUnexpectedEOF) } - continue - } - _, err = io.Copy(io.Discard, r) - if ferr, ok := err.(*flate.ReadError); ok { - err = ferr.Err - } - if err != io.ErrUnexpectedEOF { - t.Errorf("io.Copy(%d) on truncated stream: got %v, want %v", i, err, io.ErrUnexpectedEOF) } } } -- 2.50.0