From 36d1f236610a436bcbceca11e040a65593f1f253 Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Sat, 27 Aug 2022 12:42:26 +0930 Subject: [PATCH] debug/macho: use saferio to allocate Load and Symbol slices Avoid allocating large amounts of memory for corrupt input. No test case because the problem can only happen for invalid data. Let the fuzzer find cases like this. Change-Id: I2d1745200611f0af06ca58adcc3e2309ad6742d8 Reviewed-on: https://go-review.googlesource.com/c/go/+/425882 Run-TryBot: Dan Kortschak Auto-Submit: Dan Kortschak Run-TryBot: Ian Lance Taylor Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot Reviewed-by: Heschi Kreinick Auto-Submit: Ian Lance Taylor --- src/debug/macho/file.go | 57 ++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/debug/macho/file.go b/src/debug/macho/file.go index e6b170a7cd..e35b4df508 100644 --- a/src/debug/macho/file.go +++ b/src/debug/macho/file.go @@ -253,9 +253,13 @@ func NewFile(r io.ReaderAt) (*File, error) { if _, err := r.ReadAt(dat, offset); err != nil { return nil, err } - f.Loads = make([]Load, f.Ncmd) + c := saferio.SliceCap([]Load{}, uint64(f.Ncmd)) + if c < 0 { + return nil, &FormatError{offset, "too many load commands", nil} + } + f.Loads = make([]Load, 0, c) bo := f.ByteOrder - for i := range f.Loads { + for i := uint32(0); i < f.Ncmd; i++ { // Each load command begins with uint32 command and length. if len(dat) < 8 { return nil, &FormatError{offset, "command block too small", nil} @@ -270,7 +274,7 @@ func NewFile(r io.ReaderAt) (*File, error) { var s *Segment switch cmd { default: - f.Loads[i] = LoadBytes(cmddat) + f.Loads = append(f.Loads, LoadBytes(cmddat)) case LoadCmdRpath: var hdr RpathCmd @@ -284,7 +288,7 @@ func NewFile(r io.ReaderAt) (*File, error) { } l.Path = cstring(cmddat[hdr.Path:]) l.LoadBytes = LoadBytes(cmddat) - f.Loads[i] = l + f.Loads = append(f.Loads, l) case LoadCmdDylib: var hdr DylibCmd @@ -301,7 +305,7 @@ func NewFile(r io.ReaderAt) (*File, error) { l.CurrentVersion = hdr.CurrentVersion l.CompatVersion = hdr.CompatVersion l.LoadBytes = LoadBytes(cmddat) - f.Loads[i] = l + f.Loads = append(f.Loads, l) case LoadCmdSymtab: var hdr SymtabCmd @@ -319,15 +323,15 @@ func NewFile(r io.ReaderAt) (*File, error) { } else { symsz = 12 } - symdat := make([]byte, int(hdr.Nsyms)*symsz) - if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil { + symdat, err := saferio.ReadDataAt(r, uint64(hdr.Nsyms)*uint64(symsz), int64(hdr.Symoff)) + if err != nil { return nil, err } st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset) if err != nil { return nil, err } - f.Loads[i] = st + f.Loads = append(f.Loads, st) f.Symtab = st case LoadCmdDysymtab: @@ -357,7 +361,7 @@ func NewFile(r io.ReaderAt) (*File, error) { st.LoadBytes = LoadBytes(cmddat) st.DysymtabCmd = hdr st.IndirectSyms = x - f.Loads[i] = st + f.Loads = append(f.Loads, st) f.Dysymtab = st case LoadCmdSegment: @@ -379,7 +383,7 @@ func NewFile(r io.ReaderAt) (*File, error) { s.Prot = seg32.Prot s.Nsect = seg32.Nsect s.Flag = seg32.Flag - f.Loads[i] = s + f.Loads = append(f.Loads, s) for i := 0; i < int(s.Nsect); i++ { var sh32 Section32 if err := binary.Read(b, bo, &sh32); err != nil { @@ -419,7 +423,7 @@ func NewFile(r io.ReaderAt) (*File, error) { s.Prot = seg64.Prot s.Nsect = seg64.Nsect s.Flag = seg64.Flag - f.Loads[i] = s + f.Loads = append(f.Loads, s) for i := 0; i < int(s.Nsect); i++ { var sh64 Section64 if err := binary.Read(b, bo, &sh64); err != nil { @@ -441,6 +445,12 @@ func NewFile(r io.ReaderAt) (*File, error) { } } if s != nil { + if int64(s.Offset) < 0 { + return nil, &FormatError{offset, "invalid section offset", s.Offset} + } + if int64(s.Filesz) < 0 { + return nil, &FormatError{offset, "invalid section file size", s.Filesz} + } s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz)) s.ReaderAt = s.sr } @@ -450,9 +460,13 @@ func NewFile(r io.ReaderAt) (*File, error) { func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) { bo := f.ByteOrder - symtab := make([]Symbol, hdr.Nsyms) + c := saferio.SliceCap([]Symbol{}, uint64(hdr.Nsyms)) + if c < 0 { + return nil, &FormatError{offset, "too many symbols", nil} + } + symtab := make([]Symbol, 0, c) b := bytes.NewReader(symdat) - for i := range symtab { + for i := 0; i < int(hdr.Nsyms); i++ { var n Nlist64 if f.Magic == Magic64 { if err := binary.Read(b, bo, &n); err != nil { @@ -469,7 +483,6 @@ func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset n.Desc = n32.Desc n.Value = uint64(n32.Value) } - sym := &symtab[i] if n.Name >= uint32(len(strtab)) { return nil, &FormatError{offset, "invalid name in symbol table", n.Name} } @@ -478,11 +491,13 @@ func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset if strings.Contains(name, ".") && name[0] == '_' { name = name[1:] } - sym.Name = name - sym.Type = n.Type - sym.Sect = n.Sect - sym.Desc = n.Desc - sym.Value = n.Value + symtab = append(symtab, Symbol{ + Name: name, + Type: n.Type, + Sect: n.Sect, + Desc: n.Desc, + Value: n.Value, + }) } st := new(Symtab) st.LoadBytes = LoadBytes(cmddat) @@ -501,8 +516,8 @@ func (f *File) pushSection(sh *Section, r io.ReaderAt) error { sh.ReaderAt = sh.sr if sh.Nreloc > 0 { - reldat := make([]byte, int(sh.Nreloc)*8) - if _, err := r.ReadAt(reldat, int64(sh.Reloff)); err != nil { + reldat, err := saferio.ReadDataAt(r, uint64(sh.Nreloc)*8, int64(sh.Reloff)) + if err != nil { return err } b := bytes.NewReader(reldat)