elf_newscn would just add a new section without making sure all
existing section data was read in. This would confuse other library
code that tried to make sure all section data was read. The number of
sections would no longer match the number of sections on disk and the
internal load_shdr_wrlock would try to read too many section data from
disk (and fail because the data wasn't there). In particular this
would confuse elf_update which assumed that all section data is there.
* libelf/elf_newscn.c (elf_newscn): Explicitly get the data
for section one (if it exists) to load all existing section
data into memory.
* tests/addnewscn.c: New test.
* tests/run-addnewscn.sh: New test runner.
* Makefile.am (check_PROGRAMS): Add addnewscn.
(TESTS): Add run-addnewscn.sh.
(EXTRA_DIST): Likewise.
(addnewscn_LDADD): New variable.
https://sourceware.org/bugzilla/show_bug.cgi?id=34412
Signed-off-by: Mark Wielaard <[email protected]>
---
libelf/elf_newscn.c | 23 ++++++
tests/Makefile.am | 4 ++
tests/addnewscn.c | 159 +++++++++++++++++++++++++++++++++++++++++
tests/run-addnewscn.sh | 28 ++++++++
4 files changed, 214 insertions(+)
create mode 100644 tests/addnewscn.c
create mode 100755 tests/run-addnewscn.sh
diff --git a/libelf/elf_newscn.c b/libelf/elf_newscn.c
index f7b20ab7cb2d..d7bc9054e34f 100644
--- a/libelf/elf_newscn.c
+++ b/libelf/elf_newscn.c
@@ -1,5 +1,6 @@
/* Append new section.
Copyright (C) 1998,1999,2000,2001,2002,2005,2009,2014,2015 Red Hat, Inc.
+ Copyright (C) 2026 Mark J. Wielaard <[email protected]>
This file is part of elfutils.
Written by Ulrich Drepper <[email protected]>, 1998.
@@ -60,6 +61,28 @@ elf_newscn (Elf *elf)
rwlock_wrlock (elf->lock);
+ /* Make sure any existing sections have been read in. */
+ struct Elf_Scn *scn1 = NULL;
+ Elf_ScnList *list = (elf->class == ELFCLASS32
+ ? &elf->state.elf32.scns
+ : &elf->state.elf64.scns);
+
+ /* Find the first section. */
+ if (list->cnt > 1)
+ scn1 = &list->data[1];
+ else if (list->next != NULL)
+ scn1 = &list->next->data[0];
+
+ /* Load the section headers if necessary. Getting the header for
+ one section loads the headers for all sections. */
+ if (scn1 != NULL)
+ {
+ if (elf->class == ELFCLASS32 && scn1->shdr.e32 == NULL)
+ (void) __elf32_getshdr_wrlock (scn1);
+ else if (elf->class == ELFCLASS64 && scn1->shdr.e64 == NULL)
+ (void) __elf64_getshdr_wrlock (scn1);
+ }
+
again:
if (elf->state.elf.scns_last->cnt < elf->state.elf.scns_last->max)
{
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 137e96163928..ac21c59aab8e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -60,6 +60,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames
sectiondump \
fillfile dwarf_default_lower_bound \
dwarf_language_lower_bound dwarf-die-addr-die \
dwarf-type \
+ addnewscn \
get-units-invalid get-units-split attr-integrate-skel \
all-dwarf-ranges unit-info next_cfi \
elfcopy addsections xlate_notes elfrdwrnop \
@@ -218,6 +219,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile
test-nlist \
dwarf_language_lower_bound \
run-dwarf-die-addr-die.sh \
run-dwarf-type.sh \
+ run-addnewscn.sh \
run-get-units-invalid.sh run-get-units-split.sh \
run-attr-integrate-skel.sh \
run-all-dwarf-ranges.sh run-unit-info.sh \
@@ -603,6 +605,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
testfile-m68k-core.bz2 testfile-m68k.bz2 testfile-m68k-s.bz2 \
run-dwarf-die-addr-die.sh \
run-dwarf-type.sh \
+ run-addnewscn.sh \
run-get-units-invalid.sh run-get-units-split.sh \
testfile-hello4.dwo.bz2 testfile-hello5.dwo.bz2 \
testfile-splitdwarf-4.bz2 testfile-splitdwarf-5.bz2 \
@@ -808,6 +811,7 @@ endif
libebl = ../libebl/libebl.a ../backends/libebl_backends.a ../libcpu/libcpu.a
libeu = ../lib/libeu.a
+addnewscn_LDADD = $(libelf)
arextract_LDADD = $(libelf)
arsymtest_LDADD = $(libelf)
ar_extract_ar_LDADD = $(libelf)
diff --git a/tests/addnewscn.c b/tests/addnewscn.c
new file mode 100644
index 000000000000..a3ba063b4e81
--- /dev/null
+++ b/tests/addnewscn.c
@@ -0,0 +1,159 @@
+/* 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/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+int
+main (int argc, char **argv)
+{
+ if (argc < 2)
+ {
+ fprintf (stderr, "Need at least one argument\n");
+ exit (1);
+ }
+
+ if (elf_version (EV_CURRENT) == EV_NONE)
+ {
+ fprintf (stderr, "elf_version %s\n", elf_errmsg (-1));
+ exit (1);
+ }
+
+ const char *file = argv[1];
+ int fd = open (file, O_RDWR);
+ if (fd < 0)
+ {
+ fprintf (stderr, "Couldn't open file '%s': %s\n",
+ file, strerror (errno));
+ exit (1);
+ }
+
+ Elf *elf = elf_begin (fd, ELF_C_RDWR, NULL);
+ if (elf == NULL)
+ {
+ fprintf (stderr, "Couldn't open ELF file '%s': %s\n",
+ file, elf_errmsg (-1));
+ exit (1);
+ }
+
+ Elf_Scn *section = elf_newscn (elf);
+ if (section == NULL)
+ {
+ printf ("cannot create new section: %s\n", elf_errmsg (-1));
+ exit (1);
+ }
+
+ Elf_Data *data = elf_newdata (section);
+ data->d_buf = "frob";
+ data->d_size = strlen ("frob") + 1;
+
+ GElf_Shdr shdr;
+ if (gelf_getshdr (section, &shdr) == NULL)
+ {
+ printf ("cannot get shdr of new section: %s\n", elf_errmsg (-1));
+ exit (1);
+ }
+
+ shdr.sh_name = 0;
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_flags = SHF_STRINGS;
+ shdr.sh_addr = 0;
+ shdr.sh_offset = 0;
+ shdr.sh_size = 0;
+ shdr.sh_link = 0;
+ shdr.sh_info = 0;
+ shdr.sh_addralign = 4;
+ shdr.sh_entsize = 1;
+
+ if (gelf_update_shdr (section, &shdr) == 0)
+ {
+ printf ("cannot update section header: %s\n", elf_errmsg (-1));
+ exit (1);
+ }
+
+ if (elf_update (elf, ELF_C_WRITE) < 0)
+ {
+ printf ("cannot update elf: %s\n", elf_errmsg (-1));
+ exit (1);
+ }
+
+ elf_end (elf);
+ close (fd);
+
+ /* Reopen. Recheck. */
+ fd = open (file, O_RDONLY);
+ if (fd < 0)
+ {
+ fprintf (stderr, "Couldn't reopen file '%s': %s\n",
+ file, strerror (errno));
+ exit (1);
+ }
+
+ elf = elf_begin (fd, ELF_C_RDWR, NULL);
+ if (elf == NULL)
+ {
+ fprintf (stderr, "Couldn't reopen ELF file '%s': %s\n",
+ file, elf_errmsg (-1));
+ exit (1);
+ }
+
+ size_t shnum;
+ if (elf_getshdrnum (elf, &shnum) < 0)
+ {
+ printf ("couldn't get shnum: %s\n", elf_errmsg (-1));
+ exit (1);
+ }
+
+ section = elf_getscn (elf, shnum -1 );
+ if (section == NULL)
+ {
+ printf ("cannot get last section: %s\n", elf_errmsg (-1));
+ exit (1);
+ }
+
+ if (gelf_getshdr (section, &shdr) == NULL)
+ {
+ printf ("cannot get shdr of last section: %s\n", elf_errmsg (-1));
+ exit (1);
+ }
+
+ /* Must be the same as set. */
+ assert (shdr.sh_name == 0);
+ assert (shdr.sh_type == SHT_PROGBITS);
+ assert (shdr.sh_flags == SHF_STRINGS);
+ assert (shdr.sh_addr == 0);
+ assert (shdr.sh_link == 0);
+ assert (shdr.sh_info == 0);
+ assert (shdr.sh_addralign == 4);
+ assert (shdr.sh_entsize == 1);
+
+ elf_end (elf);
+ close (fd);
+
+ return 0;
+}
diff --git a/tests/run-addnewscn.sh b/tests/run-addnewscn.sh
new file mode 100755
index 000000000000..d9ff98ac2a72
--- /dev/null
+++ b/tests/run-addnewscn.sh
@@ -0,0 +1,28 @@
+#!/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
+
+# Four small 32/64, big/little endian ELF files to add an section to
+testfiles testfile_gnu_props.32be.o testfile_gnu_props.32le.o
+testfiles testfile_gnu_props.64be.o testfile_gnu_props.64le.o
+
+testrun ${abs_builddir}/addnewscn testfile_gnu_props.32le.o
+testrun ${abs_builddir}/addnewscn testfile_gnu_props.32be.o
+testrun ${abs_builddir}/addnewscn testfile_gnu_props.64le.o
+testrun ${abs_builddir}/addnewscn testfile_gnu_props.64be.o
+
--
2.55.0