https://sourceware.org/bugzilla/show_bug.cgi?id=34353
Bug ID: 34353
Summary: objcopy: null-pointer dereference in
bfd_set_section_size (bfd/section.c:1433)
Product: binutils
Version: 2.47 (HEAD)
Status: UNCONFIRMED
Severity: normal
Priority: P2
Component: binutils
Assignee: unassigned at sourceware dot org
Reporter: lswang1112 at gmail dot com
Target Milestone: ---
Created attachment 16819
--> https://sourceware.org/bugzilla/attachment.cgi?id=16819&action=edit
Minimal 488-byte ELF triggering the NULL deref
Affected version: GNU Binutils 2.46.50.20260703
(upstream HEAD, git commit d5a65d4c5acf91b6fa4223e930b3efd0fa8f4753)
Confirmed reproduced on latest upstream HEAD as of 2026-07-05.
------------------------------------------------------------------------
ROOT CAUSE
------------------------------------------------------------------------
copy_object() in binutils/objcopy.c handles the --update-section option
by iterating over the update_sections list. For each entry it retrieves
the corresponding section from the input BFD and then reads its
output_section pointer:
/* binutils/objcopy.c:3076-3087 */
for (pupdate = update_sections;
pupdate != NULL;
pupdate = pupdate->next)
{
pupdate->section = bfd_get_section_by_name (ibfd, pupdate->name);
if (pupdate->section == NULL)
{
non_fatal (_("error: %s not found, can't be updated"),
pupdate->name);
return false;
}
osec = pupdate->section->output_section; /* line 3081 -- may be
NULL */
if (!bfd_set_section_size (osec, pupdate->size)) /* line 3082 --
crashes */
{
bfd_nonfatal_message (NULL, obfd, osec, NULL);
return false;
}
}
bfd_get_section_by_name() succeeds (section exists in the input), but
output_section is NULL when the named section is of type SHT_GROUP.
BFD does not assign an output section to SHT_GROUP sections during the
copy setup phase (setup_section()), so output_section remains NULL.
bfd_set_section_size() then unconditionally dereferences sec->owner at
line 1433, causing a NULL pointer dereference and SIGSEGV.
A crafted ELF file with a section whose name matches the --update-section
argument and whose sh_type is SHT_GROUP (17) is sufficient to trigger the
crash.
Call chain:
objcopy --update-section .rodata=<file> poc.elf output.o
copy_main binutils/objcopy.c:6256
copy_file binutils/objcopy.c:4109
copy_object binutils/objcopy.c:3082 <-- osec = NULL
bfd_set_section_size bfd/section.c:1433 <-- NULL deref, SIGSEGV
------------------------------------------------------------------------
CRASH OUTPUT
------------------------------------------------------------------------
Build from latest HEAD (see HOW TO REPRODUCE below) with ASan+UBSan:
../../bfd/section.c:1433:10: runtime error: member access within null
pointer of type 'struct asection'
AddressSanitizer:DEADLYSIGNAL
=================================================================
==PID==ERROR: AddressSanitizer: SEGV on unknown address 0x0000000000d0
==PID==The signal is caused by a READ memory access.
==PID==Hint: address points to the zero page.
#0 bfd_set_section_size ../../bfd/section.c:1433
#1 copy_object ../../binutils/objcopy.c:3082
#2 copy_file ../../binutils/objcopy.c:4109
#3 copy_main ../../binutils/objcopy.c:6256
#4 main ../../binutils/objcopy.c:6360
SUMMARY: AddressSanitizer: SEGV ../../bfd/section.c:1433
in bfd_set_section_size
gdb backtrace at the fault, plain (non-instrumented) build:
Program received signal SIGSEGV, Segmentation fault.
0x... in bfd_set_section_size (sec=0x0, val=5) at ../../bfd/section.c:1433
1433 if (sec->owner == NULL || sec->owner->output_has_begun)
#0 bfd_set_section_size ../../bfd/section.c:1433
#1 copy_object ../../binutils/objcopy.c:3082
#2 copy_file ../../binutils/objcopy.c:4109
#3 copy_main ../../binutils/objcopy.c:6256
#4 main ../../binutils/objcopy.c:6360
(gdb) print/x sec
$1 = 0x0
------------------------------------------------------------------------
HOW TO REPRODUCE
------------------------------------------------------------------------
Build from latest HEAD:
git clone --depth 1 https://sourceware.org/git/binutils-gdb.git
cd binutils-gdb
mkdir build && cd build
../configure --disable-gdb --disable-gdbserver --disable-gdbsupport \
--disable-sim --disable-gprofng \
CFLAGS="-g -O1 -fsanitize=address,undefined"
make -j$(nproc) all-binutils
The attached poc.elf is a hand-crafted, 488-byte minimal ELF64. It
contains exactly five sections:
[0] NULL (required)
[1] .rodata SHT_GROUP, sh_link=2 (.symtab), sh_info=1,
sh_entsize=4, content=0x00000001 (GRP_COMDAT flag)
[2] .symtab two entries (NULL + one GLOBAL NOTYPE symbol)
[3] .strtab symbol name table
[4] .shstrtab section name string table
The critical property: section [1] is named ".rodata" but its sh_type is
SHT_GROUP (17). BFD accepts the file as a valid ELF but never assigns
output_section for GROUP-typed sections. When --update-section .rodata=<file>
is passed, copy_object() finds the section, reads output_section = NULL,
and passes NULL to bfd_set_section_size().
Create the update data file (any non-empty file works):
echo "A" > data.bin
Run:
./binutils/objcopy --update-section .rodata=data.bin poc.elf output.o
Under gdb:
gdb -q -batch -ex run -ex bt \
--args ./binutils/objcopy --update-section .rodata=data.bin poc.elf
output.o
Plain (no-sanitizer) build crashes with SIGSEGV, as shown above.
------------------------------------------------------------------------
SUGGESTED FIX
------------------------------------------------------------------------
Add a NULL check for osec before passing it to bfd_set_section_size().
When output_section is NULL the section will not appear in the output,
so updating its size is meaningless; emit a diagnostic and bail:
--- a/binutils/objcopy.c
+++ b/binutils/objcopy.c
@@ -3079,6 +3079,13 @@ copy_object (bfd *ibfd, bfd *obfd, const
bfd_arch_info_type *input_arch)
osec = pupdate->section->output_section;
+ if (osec == NULL)
+ {
+ non_fatal (_("error: %s has no output section, can't be
updated"),
+ pupdate->name);
+ return false;
+ }
if (!bfd_set_section_size (osec, pupdate->size))
Alternatively, the root issue can be fixed more broadly in setup_section()
(or filter_sections()) to ensure that --update-section targets whose
output_section is NULL are caught before copy_object() processes the
update list.
------------------------------------------------------------------------
Build & Platform
------------------------------------------------------------------------
binutils version: GNU Binutils 2.46.50.20260703
(git HEAD d5a65d4c5acf91b6fa4223e930b3efd0fa8f4753)
component: objcopy / libbfd
OS: Ubuntu 24.04.4 LTS
arch: x86_64
------------------------------------------------------------------------
IMPACT
------------------------------------------------------------------------
NULL pointer dereference (CWE-476) in libbfd, triggered when objcopy's
--update-section option names a section whose sh_type is SHT_GROUP in
the input ELF. Reproducible impact is denial of service (SIGSEGV).
objcopy is commonly run as part of build toolchains on third-party object
files (e.g. post-link binary patching, debug-section injection). A
malicious or malformed object file placed in a build pipeline can crash
the build host's objcopy unconditionally. Because objcopy is also invoked
by ld and other build tools on untrusted input in some workflows, the
crash surface extends beyond interactive use.
--
You are receiving this mail because:
You are on the CC list for the bug.