Skip to content

Commit 74242ba

Browse files
rolandshoemakerneild
authored andcommitted
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. Fixes #46242 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]>
1 parent f22ec51 commit 74242ba

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
@@ -96,7 +96,15 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
9696
return err
9797
}
9898
z.r = r
99-
z.File = make([]*File, 0, end.directoryRecords)
99+
// Since the number of directory records is not validated, it is not
100+
// safe to preallocate z.File without first checking that the specified
101+
// number of files is reasonable, since a malformed archive may
102+
// indicate it contains up to 1 << 128 - 1 files. Since each file has a
103+
// header which will be _at least_ 30 bytes we can safely preallocate
104+
// if (data size / 30) >= end.directoryRecords.
105+
if (uint64(size)-end.directorySize)/30 >= end.directoryRecords {
106+
z.File = make([]*File, 0, end.directoryRecords)
107+
}
100108
z.Comment = end.comment
101109
rs := io.NewSectionReader(r, 0, size)
102110
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
@@ -1325,3 +1325,62 @@ func TestReadDataDescriptor(t *testing.T) {
13251325
})
13261326
}
13271327
}
1328+
1329+
func TestCVE202133196(t *testing.T) {
1330+
// Archive that indicates it has 1 << 128 -1 files,
1331+
// this would previously cause a panic due to attempting
1332+
// to allocate a slice with 1 << 128 -1 elements.
1333+
data := []byte{
1334+
0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08,
1335+
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1336+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1337+
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02,
1338+
0x03, 0x62, 0x61, 0x65, 0x03, 0x04, 0x00, 0x00,
1339+
0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0xbe, 0x20,
1340+
0x5c, 0x6c, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00,
1341+
0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00,
1342+
0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00,
1343+
0x00, 0x00, 0xbe, 0x20, 0x5c, 0x6c, 0x09, 0x00,
1344+
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00,
1345+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1346+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1347+
0x01, 0x02, 0x03, 0x50, 0x4b, 0x06, 0x06, 0x2c,
1348+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d,
1349+
0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1350+
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
1351+
0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
1352+
0xff, 0xff, 0xff, 0x31, 0x00, 0x00, 0x00, 0x00,
1353+
0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00,
1354+
0x00, 0x00, 0x00, 0x50, 0x4b, 0x06, 0x07, 0x00,
1355+
0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00,
1356+
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50,
1357+
0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0xff,
1358+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1359+
0xff, 0xff, 0xff, 0x00, 0x00,
1360+
}
1361+
_, err := NewReader(bytes.NewReader(data), int64(len(data)))
1362+
if err != ErrFormat {
1363+
t.Fatalf("unexpected error, got: %v, want: %v", err, ErrFormat)
1364+
}
1365+
1366+
// Also check that an archive containing a handful of empty
1367+
// files doesn't cause an issue
1368+
b := bytes.NewBuffer(nil)
1369+
w := NewWriter(b)
1370+
for i := 0; i < 5; i++ {
1371+
_, err := w.Create("")
1372+
if err != nil {
1373+
t.Fatalf("Writer.Create failed: %s", err)
1374+
}
1375+
}
1376+
if err := w.Close(); err != nil {
1377+
t.Fatalf("Writer.Close failed: %s", err)
1378+
}
1379+
r, err := NewReader(bytes.NewReader(b.Bytes()), int64(b.Len()))
1380+
if err != nil {
1381+
t.Fatalf("NewReader failed: %s", err)
1382+
}
1383+
if len(r.File) != 5 {
1384+
t.Errorf("Archive has unexpected number of files, got %d, want 5", len(r.File))
1385+
}
1386+
}

0 commit comments

Comments
 (0)