1
0
mirror of https://github.com/golang/go synced 2024-11-18 00:24:48 -07:00

compress/gzip: only encode MTIME if it is valid

The GZIP format records the ModTime as an uint32 counting seconds since
the Unix epoch. The zero value is explicitly defined in section 2.3.1
as meaning no timestamp is available.

Currently, the Writer always encodes the ModTime even if it is the zero
time.Time value, which causes the Writer to try and encode the value
-62135596800 into the uint32 MTIME field. This causes an overflow and
results in our GZIP files having MTIME fields indicating a date in 2042-07-13.

We alter the Writer to only encode ModTime if the value does not underflow
the MTIME field (i.e., it is newer than the Unix epoch). We do not attempt
to fix what happens when the timestamp overflows in the year 2106.

We alter the Reader to only decode ModTime if the value is non-zero.
There is no risk of overflowing time.Time when decoding.

Fixes #17663

Change-Id: Ie1b65770c6342cd7b14aeebe10e5a49e6c9eb730
Reviewed-on: https://go-review.googlesource.com/32325
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Joe Tsai 2016-10-29 12:25:20 -07:00 committed by Joe Tsai
parent 4b130f92d2
commit b4e714e59e
4 changed files with 22 additions and 11 deletions

View File

@ -186,7 +186,11 @@ func (z *Reader) readHeader() (hdr Header, err error) {
return hdr, ErrHeader return hdr, ErrHeader
} }
flg := z.buf[3] flg := z.buf[3]
hdr.ModTime = time.Unix(int64(le.Uint32(z.buf[4:8])), 0) if t := int64(le.Uint32(z.buf[4:8])); t > 0 {
// Section 2.3.1, the zero value for MTIME means that the
// modified time is not set.
hdr.ModTime = time.Unix(t, 0)
}
// z.buf[8] is XFL and is currently ignored. // z.buf[8] is XFL and is currently ignored.
hdr.OS = z.buf[9] hdr.OS = z.buf[9]
z.digest = crc32.ChecksumIEEE(z.buf[:10]) z.digest = crc32.ChecksumIEEE(z.buf[:10])

View File

@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"hash/crc32" "hash/crc32"
"io" "io"
"time"
) )
// These constants are copied from the flate package, so that code that imports // These constants are copied from the flate package, so that code that imports
@ -143,10 +144,7 @@ func (z *Writer) Write(p []byte) (int, error) {
// Write the GZIP header lazily. // Write the GZIP header lazily.
if !z.wroteHeader { if !z.wroteHeader {
z.wroteHeader = true z.wroteHeader = true
z.buf[0] = gzipID1 z.buf = [10]byte{0: gzipID1, 1: gzipID2, 2: gzipDeflate}
z.buf[1] = gzipID2
z.buf[2] = gzipDeflate
z.buf[3] = 0
if z.Extra != nil { if z.Extra != nil {
z.buf[3] |= 0x04 z.buf[3] |= 0x04
} }
@ -156,13 +154,15 @@ func (z *Writer) Write(p []byte) (int, error) {
if z.Comment != "" { if z.Comment != "" {
z.buf[3] |= 0x10 z.buf[3] |= 0x10
} }
le.PutUint32(z.buf[4:8], uint32(z.ModTime.Unix())) if z.ModTime.After(time.Unix(0, 0)) {
// Section 2.3.1, the zero value for MTIME means that the
// modified time is not set.
le.PutUint32(z.buf[4:8], uint32(z.ModTime.Unix()))
}
if z.level == BestCompression { if z.level == BestCompression {
z.buf[8] = 2 z.buf[8] = 2
} else if z.level == BestSpeed { } else if z.level == BestSpeed {
z.buf[8] = 4 z.buf[8] = 4
} else {
z.buf[8] = 0
} }
z.buf[9] = z.OS z.buf[9] = z.OS
n, z.err = z.w.Write(z.buf[:10]) n, z.err = z.w.Write(z.buf[:10])

View File

@ -8,6 +8,7 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"reflect"
"testing" "testing"
"time" "time"
) )
@ -24,6 +25,9 @@ func TestEmpty(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("NewReader: %v", err) t.Fatalf("NewReader: %v", err)
} }
if want := (Header{OS: 255}); !reflect.DeepEqual(r.Header, want) {
t.Errorf("Header mismatch:\ngot %#v\nwant %#v", r.Header, want)
}
b, err := ioutil.ReadAll(r) b, err := ioutil.ReadAll(r)
if err != nil { if err != nil {
t.Fatalf("ReadAll: %v", err) t.Fatalf("ReadAll: %v", err)

View File

@ -7,7 +7,6 @@ import (
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
"time"
) )
// Per golang.org/issue/14937, check that every .gz file // Per golang.org/issue/14937, check that every .gz file
@ -16,8 +15,12 @@ func TestGZIPFilesHaveZeroMTimes(t *testing.T) {
if testing.Short() && testenv.Builder() == "" { if testing.Short() && testenv.Builder() == "" {
t.Skip("skipping in short mode") t.Skip("skipping in short mode")
} }
goroot, err := filepath.EvalSymlinks(runtime.GOROOT())
if err != nil {
t.Fatal("error evaluating GOROOT: ", err)
}
var files []string var files []string
err := filepath.Walk(runtime.GOROOT(), func(path string, info os.FileInfo, err error) error { err = filepath.Walk(goroot, func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }
@ -53,7 +56,7 @@ func checkZeroMTime(t *testing.T, path string) {
return return
} }
defer gz.Close() defer gz.Close()
if !gz.ModTime.Equal(time.Unix(0, 0)) { if !gz.ModTime.IsZero() {
t.Errorf("gzip file %s has non-zero mtime (%s)", path, gz.ModTime) t.Errorf("gzip file %s has non-zero mtime (%s)", path, gz.ModTime)
} }
} }