When the section headers aren't at the end of the ELF file elf_update
might overwrite the section headers while creating fill between the
sections. For ELF_C_RDWR_MMAP this mostly worked correctly, but might
get the fill wrong if there was no extra space between the end of the
section and the start of the section headers. For ELF_C_RDWR the code
didn't take the shdrs into account at all and would always overwrite
them.
Fix fill_mmap to not fill from the section start, but from the
last_position if that comes after the section headers table. Introduce
a fill_file function to use if the ELF file isn't mmapped.
We already had an test program elfrdwrnop that wrote out an existing
ELF file as is. It did however not really write anything since it
didn't alter anything. The test now explicitly marks some (existing)
data as ELF_F_DIRTY to force elf_update to actually (re)write the
data. It now also does that with ELF_C_RDWR and then again with
ELF_C_RDWR_MMAP. This made tests/run-reverse-sections.sh file without
the fixes.
Also add an explicit testfile that has the sections in the middle of
some sections. And add an explicit run-rdwrnop.sh testcase for this
file.
* libelf/elf32_updatefile.c (fill_mmap): Fix fill_start after
shdr_end.
(fill_file): New function.
(updatefile): Calculate shdr_endoff. Call fill_file instead of
fill for data and after section.
* tests/elfrdwrnop.c (test): New function split off from...
(main): here. Call test twice.
* tests/run-rdwrnop.sh: New test runner.
* tests/testfile-elfsection-gap.so.bz2: New test file.
* tests/Makefile.am (TESTS): Add run-rdwrnop.sh.
(EXTRA_DIST): Add run-rdwrnop.sh and
testfile-elfsection-gap.so.bz2.
https://sourceware.org/bugzilla/show_bug.cgi?id=34468
Signed-off-by: Mark Wielaard <[email protected]>
---
libelf/elf32_updatefile.c | 47 ++++++++++++++++++++-----
tests/Makefile.am | 2 ++
tests/elfrdwrnop.c | 27 +++++++++++++--
tests/run-rdwrnop.sh | 49 +++++++++++++++++++++++++++
tests/testfile-elfsection-gap.so.bz2 | Bin 0 -> 646 bytes
5 files changed, 115 insertions(+), 10 deletions(-)
create mode 100755 tests/run-rdwrnop.sh
create mode 100755 tests/testfile-elfsection-gap.so.bz2
diff --git a/libelf/elf32_updatefile.c b/libelf/elf32_updatefile.c
index 46afa1f4e275..a4cda63630e4 100644
--- a/libelf/elf32_updatefile.c
+++ b/libelf/elf32_updatefile.c
@@ -1,5 +1,6 @@
/* Write changed data structures.
Copyright (C) 2000-2010, 2014, 2015, 2016, 2018 Red Hat, Inc.
+ Copyright (C) 2026 Mark J. Wielaard <[email protected]>
This file is part of elfutils.
Written by Ulrich Drepper <[email protected]>, 2000.
@@ -114,7 +115,7 @@ fill_mmap (size_t offset, char *last_position, char
*scn_start,
if (last_position + written != scn_start + offset
&& shdr_end < scn_start + offset)
{
- char *fill_start = MAX (shdr_end, scn_start);
+ char *fill_start = MAX (shdr_end, last_position + written);
memset (fill_start, __libelf_fill_byte,
scn_start + offset - fill_start);
}
@@ -527,6 +528,34 @@ fill (int fd, int64_t pos, size_t len, char *fillbuf,
size_t *filledp)
}
+static inline int
+fill_file (int fd, size_t offset, int64_t last_offset,
+ int64_t scn_off, int64_t shdrs_start, int64_t shdrs_end,
+ char *fillbuf, size_t *filledp)
+{
+ size_t written = 0;
+
+ if (last_offset < shdrs_start)
+ {
+ written = MIN (scn_off + offset - last_offset,
+ shdrs_start - last_offset);
+ if (fill (fd, last_offset, written, fillbuf, filledp) != 0)
+ return -1;
+ }
+
+ if (last_offset + written != scn_off + offset
+ && shdrs_end < scn_off + (int64_t) offset)
+ {
+ size_t fill_off = MAX (shdrs_end, last_offset + written);
+ if (fill (fd, fill_off, scn_off + offset - fill_off,
+ fillbuf, filledp) != 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
int
internal_function
__elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
@@ -662,6 +691,8 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo,
size_t shnum)
return 1;
int64_t shdr_offset = elf->start_offset + ehdr->e_shoff;
+ int64_t shdr_endoff = shdr_offset + shnum * ehdr->e_shentsize;
+
#undef shdr_fctp
#define shdr_fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]
@@ -722,10 +753,10 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo,
size_t shnum)
|| ((scn->flags | dl->flags | elf->flags)
& ELF_F_DIRTY) != 0))
{
- if (unlikely (fill (elf->fildes, last_offset,
- (scn_start + dl->data.d.d_off)
- - last_offset, fillbuf,
- &filled) != 0))
+ if (unlikely (fill_file (elf->fildes, dl->data.d.d_off,
+ last_offset, scn_start,
+ shdr_offset, shdr_endoff,
+ fillbuf, &filled) != 0))
{
fail_free:
free (shdr_data_mem);
@@ -797,9 +828,9 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo,
size_t shnum)
header) changed we might have to fill the gap. */
if (scn_start > last_offset && previous_scn_changed)
{
- if (unlikely (fill (elf->fildes, last_offset,
- scn_start - last_offset, fillbuf,
- &filled) != 0))
+ if (unlikely (fill_file (elf->fildes, 0, last_offset,
+ scn_start, shdr_offset, shdr_endoff,
+ fillbuf, &filled) != 0))
goto fail_free;
}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 261649fd0d09..638f13ae0278 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -225,6 +225,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile
test-nlist \
run-reloc-bpf.sh \
run-next-cfi.sh run-next-cfi-self.sh \
run-reverse-sections.sh run-reverse-sections-self.sh \
+ run-rdwrnop.sh \
run-copyadd-sections.sh \
run-copymany-be32.sh run-copymany-le32.sh \
run-copymany-be64.sh run-copymany-le64.sh \
@@ -621,6 +622,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
testfile-riscv64.bz2 testfile-riscv64-s.bz2 \
testfile-riscv64-core.bz2 \
run-reverse-sections.sh run-reverse-sections-self.sh \
+ run-rdwrnop.sh testfile-elfsection-gap.so.bz2 \
run-copyadd-sections.sh test-copymany-subr.sh \
run-copymany-be32.sh run-copymany-le32.sh \
run-copymany-be64.sh run-copymany-le64.sh \
diff --git a/tests/elfrdwrnop.c b/tests/elfrdwrnop.c
index 997150b958cd..415f4a49719c 100644
--- a/tests/elfrdwrnop.c
+++ b/tests/elfrdwrnop.c
@@ -34,6 +34,7 @@
#include ELFUTILS_HEADER(elf)
#include <gelf.h>
+static int test (const char *name, int mode);
int
main (int argc, const char *argv[])
@@ -48,8 +49,15 @@ main (int argc, const char *argv[])
elf_version (EV_CURRENT);
const char *name = argv[1];
- printf ("elfrdwrdnop %s\n", name);
+ printf ("elfrdwrdnop %s (RDWR)\n", name);
+ test (name, ELF_C_RDWR);
+ printf ("elfrdwrdnop %s (RDWR_MMAP)\n", name);
+ test (name, ELF_C_RDWR_MMAP);
+}
+static int
+test (const char *name, int elf_mode)
+{
int fd = open (name, O_RDWR);
if (fd < 0)
{
@@ -58,7 +66,7 @@ main (int argc, const char *argv[])
exit (1);
}
- Elf *elf = elf_begin (fd, ELF_C_RDWR, NULL);
+ Elf *elf = elf_begin (fd, elf_mode, NULL);
if (elf == NULL)
{
fprintf (stderr, "Couldn't open ELF file '%s': %s\n",
@@ -78,6 +86,21 @@ main (int argc, const char *argv[])
if (phnum > 0)
elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT);
+ /* Go through all sections and mark some data dirty, so it
+ has to be rewritten. */
+ Elf_Scn *scn = NULL;
+ int c = 0;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ c++;
+ if (c % 2 == 0)
+ {
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data != NULL)
+ elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY);
+ }
+ }
+
if (elf_update (elf, ELF_C_WRITE) < 0)
{
printf ("failure in elf_update: %s\n", elf_errmsg (-1));
diff --git a/tests/run-rdwrnop.sh b/tests/run-rdwrnop.sh
new file mode 100755
index 000000000000..ea3df02340a1
--- /dev/null
+++ b/tests/run-rdwrnop.sh
@@ -0,0 +1,49 @@
+#! /bin/sh
+# Copyright (C) 2026 Mark J. Wielaard <[email protected]>
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+# Create an ELF file that has the section headers in the middle of
+# the file with some sections before the section table and some after.
+# It has phdrs, so when (re)writing it we want to use ELF_F_LAYOUT.
+#
+# echo "" | gcc -shared -nostdlib -s -Wl,-soname,libshort.so \
+# -o testfile-elfsection-gap.so -xc -
+# patchelf --debug --set-soname large-testfile-soname.so.9 \
+# testfile-elfsection-gap.so
+# patching ELF file 'testfile-elfsection-gap.so'
+# new SONAME is 'large-testfile-soname.so.9'
+# SONAME is too long, resizing...
+# replacing section '.dynstr' with size 40
+# this is a dynamic library
+# last page is 0x3000
+# first page is 0x0
+# needed space is 720
+# allocating new PT_LOAD segment
+# rewriting sht from offset 0x2098 to offset 0x3000 (size 640)
+# rewriting section '.note.gnu.build-id' from offset 0x200 (size 36) to offset
0x3280 (size 36)
+# rewriting section '.dynstr' from offset 0x1038 (size 13) to offset 0x32a8
(size 40)
+# rewriting symbol table section 2
+# writing testfile-elfsection-gap.so
+
+testfiles testfile-elfsection-gap.so
+testrun ${abs_builddir}/elfrdwrnop testfile-elfsection-gap.so
+testrun ${abs_top_builddir}/src/readelf -S testfile-elfsection-gap.so \
+ | grep -q "<corrupt>" && exit 1
+echo $?
+
+exit 0
diff --git a/tests/testfile-elfsection-gap.so.bz2
b/tests/testfile-elfsection-gap.so.bz2
new file mode 100755
index
0000000000000000000000000000000000000000..6d1eb117e40b9f96fb90573b4724754dbf17c328
GIT binary patch
literal 646
zcmV;10(t#HT4*^jL0KkKS@2l13;+W-fB*mg)z!rB@o`_pJ`um?-eAB0K!E^2AV5eE
zKtcim0~x>qJ(n4$0MP;(O-G~}WB@V%0Lah)06joxdY+*4H3Xp$2ux~vp|sS%1Y!+6
zMgR!F0007D2^5MN8a+cz0iXZ?8UO$WKmY?k0VtYO@*~vH&@?mv01W`p0jH=M0Ad3m
z(Fa7iX_BBUDoq&-a{Uz?fWo{UJu)D&JrymR0hRZcQ%6Mbx_Mm{^~>Z+VR1vscNBtA
z^o0n?2Kg;Cxn@)CJgbOnM70U4#6>&BW(O4tgcYH)zv()NfcRNQ8Uv<qk_m)ZWPx0-
z5d?^ND8_gIG@B8S!dUi;SVcUnArUl!LX_K441}_DkrB3Hj1ZE?Xj!aD6WB~)BP5pq
zNd@CpS8xy;{d}}~C{Uv)DrU}!1#`<{NVh4JE8IhFlOr1%CNPC749*aYf%=aT1Z70B
zpa9Yg*y51A^@T)oqO&3oMHG6Up^m2;kVLLh3hao3pWx!z^{{NH;Yh)h2Bp0S<{nj&
zk*r06RdFiOP~A32VSrLRyvthFA&Dbqq)=$zLsm&J2@N7MT)*j+2$fldTW3%OP?Unh
z3_7&aP**JXb_%GwP!MI%Ni5W<v@ylE4W-FcBMyTFBSW3mWQ%E|b)cUK1`sZyX(fvc
zoIFcNqFAqsUNIW9{v*LVgdyUn>WhILs|m2QeJDpjLc)>sAU^d?2_b}TbKQqeP$qCO
z1>sF{VDZj<8bPh0J`3fl1#~!9s(Gdu_>`I)fRw^-%_tdEsHm{fEOykhn!*xh0pSnJ
gU#ky_a?545m@Sw9haAw;6^Hn{k}1N3hl0hJVEhXVt^fc4
literal 0
HcmV?d00001
--
2.56.0