debug/elf: prevent offset overflow

When applying relocations, a malformed ELF file can provide an offset
that, when added to the relocation size, overflows. This wrapped-around
value could then incorrectly pass the bounds check, leading to a panic
when the slice is accessed with the original large offset.

This change eliminates the manual bounds and overflow checks
and writes a relocation to slice by calling putUint.

The putUint helper function centralizes the logic for validating slice
access, correctly handling both out-of-bounds and integer overflow conditions.
This simplifies the relocation code and improves robustness when parsing
malformed ELF files.

Fixes #75516

Change-Id: I00d806bf5501a9bf70200585ba4fd0475d7b2ddc
GitHub-Last-Rev: 49144311d3
GitHub-Pull-Request: golang/go#75522
Reviewed-on: https://go-review.googlesource.com/c/go/+/705075
Reviewed-by: Florian Lehner <lehner.florian86@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
Auto-Submit: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Commit-Queue: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Jes Cok 2025-09-25 04:41:12 +00:00 committed by Gopher Robot
parent 34e67623a8
commit 5500cbf0e4

View file

@ -25,6 +25,7 @@ import (
"internal/saferio"
"internal/zstd"
"io"
"math"
"os"
"strings"
"unsafe"
@ -830,17 +831,9 @@ func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
switch t {
case R_X86_64_64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val64 := sym.Value + uint64(rela.Addend)
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
case R_X86_64_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val32 := uint32(sym.Value) + uint32(rela.Addend)
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
}
}
@ -872,12 +865,7 @@ func (f *File) applyRelocations386(dst []byte, rels []byte) error {
sym := &symbols[symNo-1]
if t == R_386_32 {
if rel.Off+4 >= uint32(len(dst)) {
continue
}
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
val += uint32(sym.Value)
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true)
}
}
@ -910,12 +898,7 @@ func (f *File) applyRelocationsARM(dst []byte, rels []byte) error {
switch t {
case R_ARM_ABS32:
if rel.Off+4 >= uint32(len(dst)) {
continue
}
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
val += uint32(sym.Value)
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true)
}
}
@ -955,17 +938,9 @@ func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error {
switch t {
case R_AARCH64_ABS64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val64 := sym.Value + uint64(rela.Addend)
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
case R_AARCH64_ABS32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val32 := uint32(sym.Value) + uint32(rela.Addend)
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
}
}
@ -1001,11 +976,7 @@ func (f *File) applyRelocationsPPC(dst []byte, rels []byte) error {
switch t {
case R_PPC_ADDR32:
if rela.Off+4 >= uint32(len(dst)) || rela.Addend < 0 {
continue
}
val32 := uint32(sym.Value) + uint32(rela.Addend)
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
putUint(f.ByteOrder, dst, uint64(rela.Off), 4, sym.Value, 0, false)
}
}
@ -1041,17 +1012,9 @@ func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error {
switch t {
case R_PPC64_ADDR64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val64 := sym.Value + uint64(rela.Addend)
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
case R_PPC64_ADDR32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val32 := uint32(sym.Value) + uint32(rela.Addend)
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
}
}
@ -1084,12 +1047,7 @@ func (f *File) applyRelocationsMIPS(dst []byte, rels []byte) error {
switch t {
case R_MIPS_32:
if rel.Off+4 >= uint32(len(dst)) {
continue
}
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
val += uint32(sym.Value)
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true)
}
}
@ -1132,17 +1090,9 @@ func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error {
switch t {
case R_MIPS_64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val64 := sym.Value + uint64(rela.Addend)
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
case R_MIPS_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val32 := uint32(sym.Value) + uint32(rela.Addend)
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
}
}
@ -1180,17 +1130,9 @@ func (f *File) applyRelocationsLOONG64(dst []byte, rels []byte) error {
switch t {
case R_LARCH_64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val64 := sym.Value + uint64(rela.Addend)
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
case R_LARCH_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val32 := uint32(sym.Value) + uint32(rela.Addend)
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
}
}
@ -1226,17 +1168,9 @@ func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error {
switch t {
case R_RISCV_64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val64 := sym.Value + uint64(rela.Addend)
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
case R_RISCV_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val32 := uint32(sym.Value) + uint32(rela.Addend)
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
}
}
@ -1272,17 +1206,9 @@ func (f *File) applyRelocationss390x(dst []byte, rels []byte) error {
switch t {
case R_390_64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val64 := sym.Value + uint64(rela.Addend)
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
case R_390_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val32 := uint32(sym.Value) + uint32(rela.Addend)
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
}
}
@ -1318,17 +1244,10 @@ func (f *File) applyRelocationsSPARC64(dst []byte, rels []byte) error {
switch t {
case R_SPARC_64, R_SPARC_UA64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val64 := sym.Value + uint64(rela.Addend)
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
case R_SPARC_32, R_SPARC_UA32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val32 := uint32(sym.Value) + uint32(rela.Addend)
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
}
}
@ -1903,3 +1822,38 @@ type nobitsSectionReader struct{}
func (*nobitsSectionReader) ReadAt(p []byte, off int64) (n int, err error) {
return 0, errors.New("unexpected read from SHT_NOBITS section")
}
// putUint writes a relocation to slice
// at offset start of length length (4 or 8 bytes),
// adding sym+addend to the existing value if readUint is true,
// or just writing sym+addend if readUint is false.
// If the write would extend beyond the end of slice, putUint does nothing.
// If the addend is negative, putUint does nothing.
// If the addition would overflow, putUint does nothing.
func putUint(byteOrder binary.ByteOrder, slice []byte, start, length, sym uint64, addend int64, readUint bool) {
if start+length > uint64(len(slice)) || math.MaxUint64-start < length {
return
}
if addend < 0 {
return
}
s := slice[start : start+length]
switch length {
case 4:
ae := uint32(addend)
if readUint {
ae += byteOrder.Uint32(s)
}
byteOrder.PutUint32(s, uint32(sym)+ae)
case 8:
ae := uint64(addend)
if readUint {
ae += byteOrder.Uint64(s)
}
byteOrder.PutUint64(s, sym+ae)
default:
panic("can't happen")
}
}