Skip to content

Commit c92adf4

Browse files
rolandshoemakerkatiehockman
authored andcommitted
[release-branch.go1.15] archive/zip: only preallocate File slice if reasonably sized
Since the number of files in the EOCD record isn't validated, it isn't safe to preallocate Reader.Files using that field. A malformed archive can indicate it contains up to 1 << 128 - 1 files. We can still safely preallocate the slice by checking if the specified number of files in the archive is reasonable, given the size of the archive. Thanks to the OSS-Fuzz project for discovering this issue and to Emmanuel Odeke for reporting it. Updates #46242 Fixes #46396 Fixes CVE-2021-33196 Change-Id: I3c76d8eec178468b380d87fdb4a3f2cb06f0ee76 Reviewed-on: https://go-review.googlesource.com/c/go/+/318909 Trust: Roland Shoemaker <[email protected]> Trust: Katie Hockman <[email protected]> Trust: Joe Tsai <[email protected]> Run-TryBot: Roland Shoemaker <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Katie Hockman <[email protected]> Reviewed-by: Joe Tsai <[email protected]> (cherry picked from commit 74242ba) Reviewed-on: https://go-review.googlesource.com/c/go/+/322949 Reviewed-by: Filippo Valsorda <[email protected]>
1 parent 31d60cd commit c92adf4

File tree

2 files changed

+68
-1
lines changed

2 files changed

+68
-1
lines changed

src/archive/zip/reader.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,15 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
8484
return err
8585
}
8686
z.r = r
87-
z.File = make([]*File, 0, end.directoryRecords)
87+
// Since the number of directory records is not validated, it is not
88+
// safe to preallocate z.File without first checking that the specified
89+
// number of files is reasonable, since a malformed archive may
90+
// indicate it contains up to 1 << 128 - 1 files. Since each file has a
91+
// header which will be _at least_ 30 bytes we can safely preallocate
92+
// if (data size / 30) >= end.directoryRecords.
93+
if (uint64(size)-end.directorySize)/30 >= end.directoryRecords {
94+
z.File = make([]*File, 0, end.directoryRecords)
95+
}
8896
z.Comment = end.comment
8997
rs := io.NewSectionReader(r, 0, size)
9098
if _, err = rs.Seek(int64(end.directoryOffset), io.SeekStart); err != nil {

src/archive/zip/reader_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,3 +1070,62 @@ func TestIssue12449(t *testing.T) {
10701070
t.Errorf("Error reading the archive: %v", err)
10711071
}
10721072
}
1073+
1074+
func TestCVE202133196(t *testing.T) {
1075+
// Archive that indicates it has 1 << 128 -1 files,
1076+
// this would previously cause a panic due to attempting
1077+
// to allocate a slice with 1 << 128 -1 elements.
1078+
data := []byte{
1079+
0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08,
1080+
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1081+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1082+
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02,
1083+
0x03, 0x62, 0x61, 0x65, 0x03, 0x04, 0x00, 0x00,
1084+
0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0xbe, 0x20,
1085+
0x5c, 0x6c, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00,
1086+
0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00,
1087+
0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00,
1088+
0x00, 0x00, 0xbe, 0x20, 0x5c, 0x6c, 0x09, 0x00,
1089+
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00,
1090+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1091+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1092+
0x01, 0x02, 0x03, 0x50, 0x4b, 0x06, 0x06, 0x2c,
1093+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d,
1094+
0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1095+
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
1096+
0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
1097+
0xff, 0xff, 0xff, 0x31, 0x00, 0x00, 0x00, 0x00,
1098+
0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00,
1099+
0x00, 0x00, 0x00, 0x50, 0x4b, 0x06, 0x07, 0x00,
1100+
0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00,
1101+
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50,
1102+
0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0xff,
1103+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1104+
0xff, 0xff, 0xff, 0x00, 0x00,
1105+
}
1106+
_, err := NewReader(bytes.NewReader(data), int64(len(data)))
1107+
if err != ErrFormat {
1108+
t.Fatalf("unexpected error, got: %v, want: %v", err, ErrFormat)
1109+
}
1110+
1111+
// Also check that an archive containing a handful of empty
1112+
// files doesn't cause an issue
1113+
b := bytes.NewBuffer(nil)
1114+
w := NewWriter(b)
1115+
for i := 0; i < 5; i++ {
1116+
_, err := w.Create("")
1117+
if err != nil {
1118+
t.Fatalf("Writer.Create failed: %s", err)
1119+
}
1120+
}
1121+
if err := w.Close(); err != nil {
1122+
t.Fatalf("Writer.Close failed: %s", err)
1123+
}
1124+
r, err := NewReader(bytes.NewReader(b.Bytes()), int64(b.Len()))
1125+
if err != nil {
1126+
t.Fatalf("NewReader failed: %s", err)
1127+
}
1128+
if len(r.File) != 5 {
1129+
t.Errorf("Archive has unexpected number of files, got %d, want 5", len(r.File))
1130+
}
1131+
}

0 commit comments

Comments
 (0)