https://sourceware.org/bugzilla/show_bug.cgi?id=34324
Paul Wang <lswang1112 at gmail dot com> changed:
What |Removed |Added
----------------------------------------------------------------------------
CC| |lswang1112 at gmail dot com
--- Comment #2 from Paul Wang <lswang1112 at gmail dot com> ---
Created attachment 16811
--> https://sourceware.org/bugzilla/attachment.cgi?id=16811&action=edit
poc.elf - alternate PoC for the same abort(), via corrupted (not missing)
DT_PLTREL entry
I independently found this exact crash through fuzzing before seeing this
report. My PoC (attached: poc.elf) reaches the same abort() at the same
line via a different malformed input than attachment 16798, which let me
pin down the precise root cause. Sharing that, plus a suggested fix.
------------------------------------------------------------------------
ROOT CAUSE
------------------------------------------------------------------------
process_relocs() resolves the "PLT" relocation kind from DT_PLTREL at
runtime:
/* binutils/readelf.c:10064-10077 */
if (rel_type == reltype_unknown)
{
if (dynamic_relocations [i].reloc != DT_JMPREL)
abort ();
switch (filedata->dynamic_info[DT_PLTREL])
{
case DT_REL: rel_type = reltype_rel; break;
case DT_RELA: rel_type = reltype_rela; break;
}
/* no default: DT_PLTREL absent/unrecognised -> silently stays
reltype_unknown */
}
switch (rel_type)
{
default:
abort (); /* <-- readelf.c:10084 */
case reltype_rel:
...
filedata->dynamic_info[DT_PLTREL] is only populated when a .dynamic
entry's d_tag is *exactly* DT_PLTREL (20):
/* binutils/readelf.c:13456-13460 */
case DT_PLTREL:
filedata->dynamic_info[entry->d_tag] = entry->d_un.d_val;
break;
If DT_PLTRELSZ is non-zero (so this code path runs) but no entry's d_tag
equals exactly 20, dynamic_info[DT_PLTREL] keeps its zero-initialised
value, matching neither DT_REL(17) nor DT_RELA(7). rel_type stays
reltype_unknown and hits the unconditional abort() at readelf.c:10084.
Both PoCs reach this same state via different inputs:
- attachment 16798: no DT_PLTREL entry at all.
- my poc.elf: a PLTREL-shaped entry IS present, but its d_tag is
0x0800000000000014 -- the low 32 bits equal DT_PLTREL (0x14), but the
polluted high bits make d_tag != 20, so the `case DT_PLTREL:` switch
arm at readelf.c:13456 never matches and the entry is silently
dropped, same as if it were absent.
So the real bug isn't "DT_PLTREL missing", it's that PLT-relocation-type
resolution has no failure path at all -- any input where DT_PLTRELSZ is
set but DT_PLTREL doesn't resolve to exactly DT_REL/DT_RELA (by omission,
corruption, or an out-of-spec tag) reaches the same abort().
`readelf -d` on my poc.elf confirms: PLTRELSZ present, no entry decodes
as DT_PLTREL. My gdb backtrace (build with debug info) confirms the exact
line: process_relocs @ readelf.c:10084 -> process_object @ 24955 ->
process_file @ 25404 -> main @ 25470 -- same frames as the original
report, one abort() site.
------------------------------------------------------------------------
SUGGESTED FIX
------------------------------------------------------------------------
Give the DT_PLTREL resolution an explicit failure path instead of letting
rel_type silently fall through to the unconditional abort():
if (rel_type == reltype_unknown)
{
if (dynamic_relocations [i].reloc != DT_JMPREL)
abort ();
switch (filedata->dynamic_info[DT_PLTREL])
{
case DT_REL:
rel_type = reltype_rel;
break;
case DT_RELA:
rel_type = reltype_rela;
break;
+ default:
+ error (_("DT_PLTRELSZ is set but DT_PLTREL is missing or"
+ " invalid -- skipping PLT relocations\n"));
+ continue;
}
}
This keeps the existing `default: abort();` in the second switch as a
true "can't happen" assertion, and turns the attacker-controlled failure
mode into a normal parse error, consistent with how readelf reports every
other malformed .dynamic field.
Reproduced on git HEAD 18ca0215 (2026-07-02), Ubuntu 24.04.4 LTS -- same
crash site as the original report on a different HEAD/OS, so this isn't
platform-specific.
--
You are receiving this mail because:
You are on the CC list for the bug.