Skip to content

Commit a0441c7

Browse files
ianlancetaylorgopherbot
authored andcommitted
encoding/gob: use saferio.SliceCap when decoding a slice
This avoids allocating an overly large slice for corrupt input. Change the saferio.SliceCap function to take a pointer to the element type, so that we can handle slices of interface types. This revealed that a couple of existing calls were actually incorrect, passing the slice type rather than the element type. No test case because the problem can only happen for invalid data. Let the fuzzer find cases like this. Fixes #55338 Change-Id: I3c1724183cc275d4981379773b0b8faa01a9cbd2 Reviewed-on: https://go-review.googlesource.com/c/go/+/433296 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Daniel Martí <mvdan@mvdan.cc> Reviewed-by: Cherry Mui <cherryyz@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Ian Lance Taylor <iant@google.com> Auto-Submit: Ian Lance Taylor <iant@google.com>
1 parent 336ce96 commit a0441c7

File tree

6 files changed

+32
-10
lines changed

6 files changed

+32
-10
lines changed

src/debug/macho/fat.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func NewFatFile(r io.ReaderAt) (*FatFile, error) {
8686

8787
// Following the fat_header comes narch fat_arch structs that index
8888
// Mach-O images further in the file.
89-
c := saferio.SliceCap(FatArch{}, uint64(narch))
89+
c := saferio.SliceCap((*FatArch)(nil), uint64(narch))
9090
if c < 0 {
9191
return nil, &FormatError{offset, "too many images", nil}
9292
}

src/debug/macho/file.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
253253
if err != nil {
254254
return nil, err
255255
}
256-
c := saferio.SliceCap([]Load{}, uint64(f.Ncmd))
256+
c := saferio.SliceCap((*Load)(nil), uint64(f.Ncmd))
257257
if c < 0 {
258258
return nil, &FormatError{offset, "too many load commands", nil}
259259
}
@@ -460,7 +460,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
460460

461461
func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
462462
bo := f.ByteOrder
463-
c := saferio.SliceCap([]Symbol{}, uint64(hdr.Nsyms))
463+
c := saferio.SliceCap((*Symbol)(nil), uint64(hdr.Nsyms))
464464
if c < 0 {
465465
return nil, &FormatError{offset, "too many symbols", nil}
466466
}

src/debug/pe/symbol.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) {
5959
if err != nil {
6060
return nil, fmt.Errorf("fail to seek to symbol table: %v", err)
6161
}
62-
c := saferio.SliceCap(COFFSymbol{}, uint64(fh.NumberOfSymbols))
62+
c := saferio.SliceCap((*COFFSymbol)(nil), uint64(fh.NumberOfSymbols))
6363
if c < 0 {
6464
return nil, errors.New("too many symbols; file may be corrupt")
6565
}

src/encoding/gob/decode.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package gob
99
import (
1010
"encoding"
1111
"errors"
12+
"internal/saferio"
1213
"io"
1314
"math"
1415
"math/bits"
@@ -514,10 +515,22 @@ func (dec *Decoder) decodeArrayHelper(state *decoderState, value reflect.Value,
514515
}
515516
instr := &decInstr{elemOp, 0, nil, ovfl}
516517
isPtr := value.Type().Elem().Kind() == reflect.Pointer
518+
ln := value.Len()
517519
for i := 0; i < length; i++ {
518520
if state.b.Len() == 0 {
519521
errorf("decoding array or slice: length exceeds input size (%d elements)", length)
520522
}
523+
if i >= ln {
524+
// This is a slice that we only partially allocated.
525+
// Grow it using append, up to length.
526+
value = reflect.Append(value, reflect.Zero(value.Type().Elem()))
527+
cp := value.Cap()
528+
if cp > length {
529+
cp = length
530+
}
531+
value.SetLen(cp)
532+
ln = cp
533+
}
521534
v := value.Index(i)
522535
if isPtr {
523536
v = decAlloc(v)
@@ -618,7 +631,11 @@ func (dec *Decoder) decodeSlice(state *decoderState, value reflect.Value, elemOp
618631
errorf("%s slice too big: %d elements of %d bytes", typ.Elem(), u, size)
619632
}
620633
if value.Cap() < n {
621-
value.Set(reflect.MakeSlice(typ, n, n))
634+
safe := saferio.SliceCap(reflect.Zero(reflect.PtrTo(typ.Elem())).Interface(), uint64(n))
635+
if safe < 0 {
636+
errorf("%s slice too big: %d elements of %d bytes", typ.Elem(), u, size)
637+
}
638+
value.Set(reflect.MakeSlice(typ, safe, safe))
622639
} else {
623640
value.SetLen(n)
624641
}

src/internal/saferio/io.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,19 @@ func ReadDataAt(r io.ReaderAt, n uint64, off int64) ([]byte, error) {
109109
//
110110
// A negative result means that the value is always too big.
111111
//
112-
// The element type is described by passing a value of that type.
112+
// The element type is described by passing a pointer to a value of that type.
113113
// This would ideally use generics, but this code is built with
114114
// the bootstrap compiler which need not support generics.
115+
// We use a pointer so that we can handle slices of interface type.
115116
func SliceCap(v any, c uint64) int {
116117
if int64(c) < 0 || c != uint64(int(c)) {
117118
return -1
118119
}
119-
size := reflect.TypeOf(v).Size()
120+
typ := reflect.TypeOf(v)
121+
if typ.Kind() != reflect.Ptr {
122+
panic("SliceCap called with non-pointer type")
123+
}
124+
size := typ.Elem().Size()
120125
if uintptr(c)*size > chunk {
121126
c = uint64(chunk / size)
122127
if c == 0 {

src/internal/saferio/io_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,14 @@ func TestReadDataAt(t *testing.T) {
105105

106106
func TestSliceCap(t *testing.T) {
107107
t.Run("small", func(t *testing.T) {
108-
c := SliceCap(0, 10)
108+
c := SliceCap((*int)(nil), 10)
109109
if c != 10 {
110110
t.Errorf("got capacity %d, want %d", c, 10)
111111
}
112112
})
113113

114114
t.Run("large", func(t *testing.T) {
115-
c := SliceCap(byte(0), 1<<30)
115+
c := SliceCap((*byte)(nil), 1<<30)
116116
if c < 0 {
117117
t.Error("SliceCap failed unexpectedly")
118118
} else if c == 1<<30 {
@@ -121,7 +121,7 @@ func TestSliceCap(t *testing.T) {
121121
})
122122

123123
t.Run("maxint", func(t *testing.T) {
124-
c := SliceCap(byte(0), 1<<63)
124+
c := SliceCap((*byte)(nil), 1<<63)
125125
if c >= 0 {
126126
t.Errorf("SliceCap returned %d, expected failure", c)
127127
}

0 commit comments

Comments
 (0)