https://sourceware.org/bugzilla/show_bug.cgi?id=34418
Bug ID: 34418
Summary: Off-by-one section-index check in bfd/vms-alpha.c;
alpha_vms_slurp_relocs(): an ETIR relocation record
with section index == section_count passes a `>'
(should be `>=') bounds check and reads one element
past the PRIV(sections) array;
Product: binutils
Version: 2.48 (HEAD)
Status: UNCONFIRMED
Severity: normal
Priority: P2
Component: binutils
Assignee: unassigned at sourceware dot org
Reporter: achintya at umail dot ucsb.edu
Target Milestone: ---
Created attachment 16855
--> https://sourceware.org/bugzilla/attachment.cgi?id=16855&action=edit
It contains a zip with a poc and a crash report
================================================================================
VERSION
================================================================================
`objdump --version` prints:
"GNU objdump (GNU Binutils) 2.47.50.20260720"
This is git master, commit cef1fbf3b02ff6e5bc62e1b8a7d30c0a32b8b9f7
(dated 2026-07-20). Per binutils' versioning convention, bfd/version.m4 =
BFD_VERSION 2.47.50 on master denotes the DEVELOPMENT version heading to the
2.48 release (the 2.47 release branch, binutils-2_47-branch, carries 2.46.90).
Affected versions: the defect is present in BOTH:
* git master (2.47.50, -> 2.48 development), commit above; and
* the binutils-2_47-branch (2.46.90, -> the upcoming 2.47 release): the same
line bfd/vms-alpha.c:5528 uses `>' there too (verified).
Not fixed as of either branch.
================================================================================
BUILD CONFIGURATION (from config.status)
================================================================================
Host/target configuration name: x86_64-pc-linux-gnu
configure options:
--disable-gdb --disable-gdbserver --disable-sim --disable-gprofng
--disable-werror --disable-nls --enable-targets=alpha-dec-vms
Compiler: clang 14.0.0 (Ubuntu 14.0.0-1ubuntu1.1), -O2.
NOTE: the alpha-vms BFD backend must be built in (it is included by
--enable-targets=all as well as by an explicit alpha*-*-*vms* target). The
defect is in BFD source and is compiler/optimization independent.
================================================================================
WHAT WENT WRONG / WHAT SHOULD HAVE HAPPENED
================================================================================
What went wrong:
While slurping relocations from an Alpha-VMS object, alpha_vms_slurp_relocs()
validates the attacker-supplied section index `cur_psect` with
if (cur_psect < 0 || cur_psect > (int) PRIV (section_count)) /* :5528
*/
Because the upper bound uses `>' instead of `>=', the value
cur_psect == section_count is accepted. Only indices 0 .. section_count-1
are
valid section pointers, so
sec = PRIV (sections)[cur_psect]; /* :5536 */
with cur_psect == section_count reads an invalid entry. PRIV(sections) is
allocated in powers-of-two chunks (section_max entries, starting at 16 and
doubling); when the number of sections is exactly a chunk boundary e.g. 16
sections, so section_count == section_max == 16 and the allocation is 128
bytes; index section_count is one asection* PAST THE END of the allocation
(the heap-buffer-overflow read demonstrated below). (At a non-boundary count
it instead reads an uninitialised in-allocation slot; either way `sec' is a
bogus pointer.) The crafted object uses exactly 16 sections to make it a
clean past-the-end read. The
resulting out-of-bounds pointer `sec' is then dereferenced vms_section_data
(sec) == sec->used_by_bfd (:5543), sec->reloc_count (:5546) and written
through vms_sec->reloc_max = 64, sec->relocation = bfd_zmalloc(...) (:5550-
5551), sec->reloc_count++ (:5564).
What should have happened:
The bounds check should reject cur_psect == section_count, i.e. use `>='
(matching the five sibling checks in the same file at lines 1351, 1369, 1403,
1500 and 5601, all of which correctly write
`>= (int) PRIV (section_count)'). A crafted index should produce
"invalid section index in ETIR" and a clean failure, not an out-of-bounds
access.
================================================================================
ROOT CAUSE (bfd/vms-alpha.c, alpha_vms_slurp_relocs)
================================================================================
5528 if (cur_psect < 0 || cur_psect > (int) PRIV (section_count)) <-- '>'
5529 { _bfd_error_handler (_("invalid section index in ETIR")); goto
fail; }
5534 if (PRIV (sections) == NULL) goto fail;
5536 sec = PRIV (sections)[cur_psect]; <-- OOB read when cur_psect
== section_count
5543 vms_sec = vms_section_data (sec); <-- sec->used_by_bfd
(deref)
5546 if (sec->reloc_count >= vms_sec->reloc_max) {
5550 vms_sec->reloc_max = 64; <-- write through wild ptr
5551 sec->relocation = bfd_zmalloc (...); <-- write through wild ptr
...
5564 sec->reloc_count++; <-- write through wild ptr
cur_psect is set from an attacker-controlled ETIR record: ETIR__C_STA_PQ
takes
a 32-bit section index (bfd_getl32(ptr+4)) into cur_psidx, and
ETIR__C_CTL_SETRB
copies it into cur_psect. With exactly 16 program sections defined in the
EGSD, PRIV(sections) is a 16-element (128-byte) allocation and section_count
== 16, so cur_psect == 16 passes `16 > 16' == false and indexes sections[16].
The sibling checks that correctly use `>=':
vms-alpha.c:1351, 1369, 1403, 1500, 5601 -> ">= (int) PRIV (section_count)"
Only line 5528 uses ">".
================================================================================
REPRODUCTION
================================================================================
Tool options at run time:
objdump -r poc_vms.obj
(The same slurp path is reached by any BFD client that canonicalizes the VMS
object's relocations, e.g. `objdump -r/-R/-d' and `ld'.)
Input file poc_vms.obj (228 bytes; sha256
8eec54268a810ba8ec437e9cefaf37876378091a9fcb91a0a7e698a923c316ac):
A minimal FF_NATIVE Alpha-VMS object EMH (module header) + EGSD defining 16
program sections (so section_count == 16) + one ETIR record whose command
stream
is STA_PQ(index=16) -> CTL_SETRB (cur_psect = 16) -> STO_OFF (reaches the
relocation-apply block) + EEOM. One section is a real, relocatable ".text" so
that objdump -r queries its relocations and triggers alpha_vms_slurp_relocs.
poc_vms.obj (base64):
CAAsAAAAAAAAAAAAAAAAAAAAAAAEVEVTVAFWICAgICAgICAgICAgICAgICAKAJIAAAAAAAAAEgAA
AIgABAAAAAUudGV4dAAACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAgA
AAAAAAAACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAA
AAAAAAAIAAAAAAAAAAgAAAAAAAsAHAADABAAEAAAAAAAAAAAAAAAlgAEADsABAAJAAoAAAAAAAAA
================================================================================
OBSERVED BEHAVIOUR
================================================================================
Under AddressSanitizer (clang -fsanitize=address):
objdump -r poc_vms.obj:
ERROR: AddressSanitizer: heap-buffer-overflow ...
READ of size 8 at 0x... thread T0
#0 alpha_vms_slurp_relocs bfd/vms-alpha.c:5536
#1 alpha_vms_get_reloc_upper_bound bfd/vms-alpha.c:5639
#2 bfd_get_reloc_upper_bound bfd/bfd.c:2198
#3 dump_relocs_in_section binutils/objdump.c:5558
#4 bfd_map_over_sections bfd/section.c:1369
#5 dump_relocs / dump_bfd binutils/objdump.c ... objdump -r
"0x... is located 0 bytes to the right of 128-byte region [...,...)"
allocated by bfd_realloc_or_free at _bfd_vms_slurp_egsd (vms-alpha.c:1298)
i.e. one asection* past the 16-entry PRIV(sections) array.
In an ordinary (non-ASan) build, clang -O2, with or without -DNDEBUG:
objdump -r poc_vms.obj -> Segmentation fault (SIGSEGV).
The out-of-bounds slot holds unrelated heap data; the wild `sec' pointer is
dereferenced at vms-alpha.c:5543 and faults. (No assertion or FORTIFY/canary
is
involved on this path it is a wild-pointer dereference.)
================================================================================
IMPACT
================================================================================
Reachable from an untrusted input file with no special privileges (running
objdump/ld etc. on an attacker-supplied Alpha-VMS object).
- Out-of-bounds read of an asection* one element past a heap allocation
[CWE-125], whose value then flows into a pointer dereference and several
writes-through (sec->relocation = ..., vms_sec->reloc_max = 64,
sec->reloc_count++) [CWE-787 potential].
Realistic impact: DENIAL OF SERVICE a reproducible crash (SIGSEGV) while
processing a crafted file, confirmed on ordinary (non-sanitizer) builds with
and
without -DNDEBUG. The out-of-bounds slot holds unrelated heap data; the wild
`sec' is dereferenced at vms-alpha.c:5543 and faults. No stack-protector /
FORTIFY/assertion is involved it is a wild-pointer dereference.
================================================================================
SUGGESTED FIX
================================================================================
Use `>=' to match the sibling checks, rejecting cur_psect == section_count:
- if (cur_psect < 0 || cur_psect > (int) PRIV (section_count))
+ if (cur_psect < 0 || cur_psect >= (int) PRIV (section_count))
{
_bfd_error_handler (_("invalid section index in ETIR"));
goto fail;
}
--
You are receiving this mail because:
You are on the CC list for the bug.