]> Cypherpunks repositories - gostls13.git/commitdiff
debug/elf: avoid decompress of section twice
authorMeng Zhuo <mzh@golangcn.org>
Sun, 2 Apr 2023 12:01:28 +0000 (20:01 +0800)
committerGopher Robot <gobot@golang.org>
Tue, 11 Apr 2023 13:16:24 +0000 (13:16 +0000)
In rare cases, elf will get a corrupted section starts with 0x1,
which happens to be COMPRESS_ZLIB that causing decompress twice.
This CL drops sectionData decompress data after open section.

Fixes #59208

Change-Id: I7999a55868b3b3481509e1ac35985f7580f0f688
Reviewed-on: https://go-review.googlesource.com/c/go/+/480895
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Run-TryBot: M Zhuo <mzh@golangcn.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
src/debug/elf/file.go
src/debug/elf/file_test.go

index 3ff5f9554b31dca86fd65c2d7413dbef5e7ceaa8..04737e6b2f42bfc1655ab06838506f1825461af3 100644 (file)
@@ -137,20 +137,46 @@ func (s *Section) Open() io.ReadSeeker {
        if s.Type == SHT_NOBITS {
                return io.NewSectionReader(&nobitsSectionReader{}, 0, int64(s.Size))
        }
+
+       var zrd func(io.Reader) (io.ReadCloser, error)
        if s.Flags&SHF_COMPRESSED == 0 {
-               return io.NewSectionReader(s.sr, 0, 1<<63-1)
-       }
-       if s.compressionType == COMPRESS_ZLIB {
-               return &readSeekerFromReader{
-                       reset: func() (io.Reader, error) {
-                               fr := io.NewSectionReader(s.sr, s.compressionOffset, int64(s.FileSize)-s.compressionOffset)
-                               return zlib.NewReader(fr)
-                       },
-                       size: int64(s.Size),
+
+               if !strings.HasPrefix(s.Name, ".zdebug") {
+                       return io.NewSectionReader(s.sr, 0, 1<<63-1)
                }
+
+               b := make([]byte, 12)
+               n, _ := s.sr.ReadAt(b, 0)
+               if n != 12 || string(b[:4]) != "ZLIB" {
+                       return io.NewSectionReader(s.sr, 0, 1<<63-1)
+               }
+
+               s.compressionOffset = 12
+               s.compressionType = COMPRESS_ZLIB
+               s.Size = binary.BigEndian.Uint64(b[4:12])
+               zrd = zlib.NewReader
+
+       } else if s.Flags&SHF_ALLOC != 0 {
+               return errorReader{&FormatError{int64(s.Offset),
+                       "SHF_COMPRESSED applies only to non-allocable sections", s.compressionType}}
+       }
+
+       switch s.compressionType {
+       case COMPRESS_ZLIB:
+               zrd = zlib.NewReader
+       }
+
+       if zrd == nil {
+               return errorReader{&FormatError{int64(s.Offset), "unknown compression type", s.compressionType}}
+       }
+
+       return &readSeekerFromReader{
+               reset: func() (io.Reader, error) {
+                       fr := io.NewSectionReader(s.sr, s.compressionOffset, int64(s.FileSize)-s.compressionOffset)
+                       return zrd(fr)
+               },
+               size: int64(s.Size),
        }
-       err := &FormatError{int64(s.Offset), "unknown compression type", s.compressionType}
-       return errorReader{err}
 }
 
 // A ProgHeader represents a single ELF program header.
@@ -1309,44 +1335,6 @@ func (f *File) DWARF() (*dwarf.Data, error) {
                if err != nil && uint64(len(b)) < s.Size {
                        return nil, err
                }
-               var dlen uint64
-               if len(b) >= 12 && string(b[:4]) == "ZLIB" {
-                       dlen = binary.BigEndian.Uint64(b[4:12])
-                       s.compressionOffset = 12
-               }
-               if dlen == 0 && len(b) >= 12 && s.Flags&SHF_COMPRESSED != 0 &&
-                       s.Flags&SHF_ALLOC == 0 &&
-                       f.FileHeader.ByteOrder.Uint32(b[:]) == uint32(COMPRESS_ZLIB) {
-                       s.compressionType = COMPRESS_ZLIB
-                       switch f.FileHeader.Class {
-                       case ELFCLASS32:
-                               // Chdr32.Size offset
-                               dlen = uint64(f.FileHeader.ByteOrder.Uint32(b[4:]))
-                               s.compressionOffset = 12
-                       case ELFCLASS64:
-                               if len(b) < 24 {
-                                       return nil, errors.New("invalid compress header 64")
-                               }
-                               // Chdr64.Size offset
-                               dlen = f.FileHeader.ByteOrder.Uint64(b[8:])
-                               s.compressionOffset = 24
-                       default:
-                               return nil, fmt.Errorf("unsupported compress header:%s", f.FileHeader.Class)
-                       }
-               }
-               if dlen > 0 {
-                       r, err := zlib.NewReader(bytes.NewBuffer(b[s.compressionOffset:]))
-                       if err != nil {
-                               return nil, err
-                       }
-                       b, err = saferio.ReadData(r, dlen)
-                       if err != nil {
-                               return nil, err
-                       }
-                       if err := r.Close(); err != nil {
-                               return nil, err
-                       }
-               }
 
                if f.Type == ET_EXEC {
                        // Do not apply relocations to DWARF sections for ET_EXEC binaries.
index f591f05a2ec4ff75b44a4459ce942eeb254f606a..7d5895b650dba47c8680ceb29e5cb62ffeb7dbbc 100644 (file)
@@ -7,6 +7,7 @@ package elf
 import (
        "bytes"
        "compress/gzip"
+       "compress/zlib"
        "debug/dwarf"
        "encoding/binary"
        "fmt"
@@ -17,6 +18,7 @@ import (
        "path"
        "reflect"
        "runtime"
+       "strings"
        "testing"
 )
 
@@ -1242,3 +1244,47 @@ func TestDynValue(t *testing.T) {
                t.Errorf("DynValue(DT_VERNEEDNUM): got %v, want [1]", vals)
        }
 }
+
+func TestIssue59208(t *testing.T) {
+       // corrupted dwarf data should raise invalid dwarf data instead of invalid zlib
+       const orig = "testdata/compressed-64.obj"
+       f, err := Open(orig)
+       if err != nil {
+               t.Fatal(err)
+       }
+       sec := f.Section(".debug_info")
+
+       data, err := os.ReadFile(orig)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       dn := make([]byte, len(data))
+       zoffset := sec.Offset + uint64(sec.compressionOffset)
+       copy(dn, data[:zoffset])
+
+       ozd, err := sec.Data()
+       if err != nil {
+               t.Fatal(err)
+       }
+       buf := bytes.NewBuffer(nil)
+       wr := zlib.NewWriter(buf)
+       // corrupt origin data same as COMPRESS_ZLIB
+       copy(ozd, []byte{1, 0, 0, 0})
+       wr.Write(ozd)
+       wr.Close()
+
+       copy(dn[zoffset:], buf.Bytes())
+       copy(dn[sec.Offset+sec.FileSize:], data[sec.Offset+sec.FileSize:])
+
+       nf, err := NewFile(bytes.NewReader(dn))
+       if err != nil {
+               t.Error(err)
+       }
+
+       const want = "decoding dwarf section info"
+       _, err = nf.DWARF()
+       if err == nil || !strings.Contains(err.Error(), want) {
+               t.Errorf("DWARF = %v; want %q", err, want)
+       }
+}