[PATCH 3/8] elf: Honor LA_SYMB_NOPLTENTER/NOPLTEXIT per auditor (BZ 34688)
Adhemerval Zanella
adhemerval.zanella@linaro.org
Mon Sep 28 18:55:07 GMT 2026
With more than one auditor, the LA_SYMB_NOPLTENTER and
LA_SYMB_NOPLTEXIT an auditor sets in la_symbind were not honored:
- _dl_audit_symbind updated the summary bits with
'enterexit &= flags & 3', which also cleared the per-auditor bits
of the previous auditors. So only the choice of the last one was
kept.
- _dl_audit_pltexit checked 'LA_SYMB_NOPLTEXIT >> (2 * cnt)', which
is the summary bit for the first auditor and zero for the others,
instead of the auditor's own bit.
Both date back to the original audit implementation.
The new test is UNSUPPORTED if la_pltexit is never called (as on mips
and hppa), and XFAIL on alpha, loongarch, riscv, and aarch64 with BTI,
where the la_pltexit path of _dl_runtime_profile crashes.
Checked on x86_64-linux-gnu, i686-linux-gnu, aarch64-linux-gnu,
arm-linux-gnueabihf, alpha-linux-gnu, hppa-linux-gnu,
loongarch64-linux-gnu, mips64el-linux-gnu, powerpc64-linux-gnu,
powerpc64le-linux-gnu, riscv64-linux-gnu, s390x-linux-gnu, and
sparc64-linux-gnu.
---
elf/Makefile | 10 +++
elf/dl-audit.c | 11 +--
elf/tst-audit-plt-flags-mod.c | 35 ++++++++++
elf/tst-audit-plt-flags.c | 124 +++++++++++++++++++++++++++++++++
elf/tst-auditmod-plt-flags-a.c | 85 ++++++++++++++++++++++
elf/tst-auditmod-plt-flags-b.c | 22 ++++++
sysdeps/aarch64/Makefile | 4 ++
sysdeps/alpha/Makefile | 4 ++
sysdeps/loongarch/Makefile | 2 +
sysdeps/riscv/Makefile | 2 +
10 files changed, 294 insertions(+), 5 deletions(-)
create mode 100644 elf/tst-audit-plt-flags-mod.c
create mode 100644 elf/tst-audit-plt-flags.c
create mode 100644 elf/tst-auditmod-plt-flags-a.c
create mode 100644 elf/tst-auditmod-plt-flags-b.c
diff --git a/elf/Makefile b/elf/Makefile
index e3419cd1185..e3dbe83cc21 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -406,6 +406,7 @@ tests += \
tst-align \
tst-align2 \
tst-align3 \
+ tst-audit-plt-flags \
tst-audit-symbind-dlsym \
tst-audit-symbind-flags \
tst-audit-tlsdesc \
@@ -913,6 +914,7 @@ modules-names += \
tst-alignmod3 \
tst-array2dep \
tst-array5dep \
+ tst-audit-plt-flags-mod \
tst-audit-symbind-dlsym-mod \
tst-audit-symbind-flags-mod \
tst-audit-tlsdesc-mod1 \
@@ -950,6 +952,8 @@ modules-names += \
tst-auditmanymod7 \
tst-auditmanymod8 \
tst-auditmanymod9 \
+ tst-auditmod-plt-flags-a \
+ tst-auditmod-plt-flags-b \
tst-auditmod-symbind-dlsym-a \
tst-auditmod-symbind-dlsym-b \
tst-auditmod-symbind-flags-a \
@@ -2884,6 +2888,12 @@ $(objpfx)tst-audit-symbind-flags.out: \
$(objpfx)tst-auditmod-symbind-flags-b.so
LDFLAGS-tst-audit-symbind-flags = -Wl,-z,lazy
+$(objpfx)tst-audit-plt-flags: $(objpfx)tst-audit-plt-flags-mod.so
+$(objpfx)tst-audit-plt-flags.out: \
+ $(objpfx)tst-auditmod-plt-flags-a.so \
+ $(objpfx)tst-auditmod-plt-flags-b.so
+LDFLAGS-tst-audit-plt-flags = -Wl,-z,lazy
+
# tst-sonamemove links against an older implementation of the library.
LDFLAGS-tst-sonamemove-linkmod1.so = \
-Wl,--version-script=tst-sonamemove-linkmod1.map \
diff --git a/elf/dl-audit.c b/elf/dl-audit.c
index cd2fa44cf14..a64f978cfb4 100644
--- a/elf/dl-audit.c
+++ b/elf/dl-audit.c
@@ -241,10 +241,11 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
}
/* Remember the results for every audit library and store a summary
- in the first two bits. */
- enterexit &= flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT);
- enterexit |= ((flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT))
- << ((cnt + 1) * 2));
+ in the first two bits, without clearing the bits of the previous
+ auditors. */
+ unsigned int noplt = flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT);
+ enterexit &= noplt | ~(LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT);
+ enterexit |= noplt << ((cnt + 1) * 2);
}
else
/* If the bind flags say this auditor is not interested, set the bits
@@ -375,7 +376,7 @@ _dl_audit_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
{
if (afct->ARCH_LA_PLTEXIT != NULL
&& (reloc_result->enterexit
- & (LA_SYMB_NOPLTEXIT >> (2 * cnt))) == 0)
+ & (LA_SYMB_NOPLTEXIT << (2 * (cnt + 1)))) == 0)
{
struct auditstate *l_state = link_map_audit_state (l, cnt);
struct auditstate *bound_state
diff --git a/elf/tst-audit-plt-flags-mod.c b/elf/tst-audit-plt-flags-mod.c
new file mode 100644
index 00000000000..c661440c994
--- /dev/null
+++ b/elf/tst-audit-plt-flags-mod.c
@@ -0,0 +1,35 @@
+/* Module for tst-audit-plt-flags.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+int
+tst_audit_plt_flags_func1 (void)
+{
+ return 1;
+}
+
+int
+tst_audit_plt_flags_func2 (void)
+{
+ return 2;
+}
+
+int
+tst_audit_plt_flags_func3 (void)
+{
+ return 3;
+}
diff --git a/elf/tst-audit-plt-flags.c b/elf/tst-audit-plt-flags.c
new file mode 100644
index 00000000000..c4562e2d74e
--- /dev/null
+++ b/elf/tst-audit-plt-flags.c
@@ -0,0 +1,124 @@
+/* Check that LA_SYMB_NOPLTENTER and LA_SYMB_NOPLTEXIT are honored per auditor.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/subprocess.h>
+#include <support/support.h>
+
+static int restart;
+#define CMDLINE_OPTIONS \
+ { "restart", no_argument, &restart, 1 },
+
+int tst_audit_plt_flags_func1 (void);
+int tst_audit_plt_flags_func2 (void);
+int tst_audit_plt_flags_func3 (void);
+
+static int
+handle_restart (void)
+{
+ TEST_COMPARE (tst_audit_plt_flags_func1 (), 1);
+ TEST_COMPARE (tst_audit_plt_flags_func2 (), 2);
+ TEST_COMPARE (tst_audit_plt_flags_func3 (), 3);
+ return 0;
+}
+
+static int
+count_lines (const char *buf, const char *line)
+{
+ size_t len = strlen (line);
+ int n = 0;
+ for (const char *p = buf; p != NULL && *p != '\0'; )
+ {
+ if (strncmp (p, line, len) == 0 && p[len] == '\n')
+ n++;
+ p = strchr (p, '\n');
+ if (p != NULL)
+ p++;
+ }
+ return n;
+}
+
+static int
+do_test (void)
+{
+ if (restart)
+ return handle_restart ();
+
+ char *audit = xasprintf ("%s/elf/tst-auditmod-plt-flags-a.so"
+ ":%s/elf/tst-auditmod-plt-flags-b.so",
+ support_objdir_root, support_objdir_root);
+ setenv ("LD_AUDIT", audit, 1);
+ free (audit);
+
+ char *program = xasprintf ("%s/elf/tst-audit-plt-flags",
+ support_objdir_root);
+ char *args[] = { program, (char *) "--direct", (char *) "--restart", NULL };
+ struct support_spawn_wrapped *w
+ = support_spawn_wrap (program, args, NULL, 0);
+ struct support_capture_subprocess result
+ = support_capture_subprogram (w->path, w->argv, w->envp);
+ support_spawn_wrapped_free (w);
+ free (program);
+ support_capture_subprocess_check (&result, "tst-audit-plt-flags", 0,
+ sc_allow_stderr);
+ const char *err = result.err.buffer;
+
+ if (strstr (err, ": la_pltexit: ") == NULL)
+ FAIL_UNSUPPORTED ("la_pltexit not supported");
+
+ /* Both auditors request la_pltexit in la_pltenter. The first one
+ sets LA_SYMB_NOPLTEXIT for func1 and LA_SYMB_NOPLTENTER for func3,
+ the second one sets LA_SYMB_NOPLTEXIT for func2. */
+ static const struct
+ {
+ const char *line;
+ int count;
+ } expected[] =
+ {
+ { "a: la_pltenter: tst_audit_plt_flags_func1", 1 },
+ { "a: la_pltexit: tst_audit_plt_flags_func1", 0 },
+ { "b: la_pltenter: tst_audit_plt_flags_func1", 1 },
+ { "b: la_pltexit: tst_audit_plt_flags_func1", 1 },
+ { "a: la_pltenter: tst_audit_plt_flags_func2", 1 },
+ { "a: la_pltexit: tst_audit_plt_flags_func2", 1 },
+ { "b: la_pltenter: tst_audit_plt_flags_func2", 1 },
+ { "b: la_pltexit: tst_audit_plt_flags_func2", 0 },
+ { "a: la_pltenter: tst_audit_plt_flags_func3", 0 },
+ { "a: la_pltexit: tst_audit_plt_flags_func3", 1 },
+ { "b: la_pltenter: tst_audit_plt_flags_func3", 1 },
+ { "b: la_pltexit: tst_audit_plt_flags_func3", 1 },
+ };
+ for (size_t j = 0; j < array_length (expected); j++)
+ if (count_lines (err, expected[j].line) != expected[j].count)
+ FAIL ("expected %d \"%s\" lines", expected[j].count,
+ expected[j].line);
+
+ if (support_record_failure_is_failed ())
+ printf ("info: auditor output:\n%s", err);
+
+ support_capture_subprocess_free (&result);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-auditmod-plt-flags-a.c b/elf/tst-auditmod-plt-flags-a.c
new file mode 100644
index 00000000000..2bf12669ba7
--- /dev/null
+++ b/elf/tst-auditmod-plt-flags-a.c
@@ -0,0 +1,85 @@
+/* Auditor for tst-audit-plt-flags.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <link.h>
+#include <stdio.h>
+#include <string.h>
+#include <tst-audit.h>
+
+#ifndef TAG
+# define TAG "a"
+/* The functions for which la_pltexit and la_pltenter are not wanted. */
+# define NOPLTEXIT_FUNC "tst_audit_plt_flags_func1"
+# define NOPLTENTER_FUNC "tst_audit_plt_flags_func3"
+#endif
+
+#define PREFIX "tst_audit_plt_flags_func"
+
+unsigned int
+la_version (unsigned int version)
+{
+ return LAV_CURRENT;
+}
+
+unsigned int
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
+{
+ return LA_FLG_BINDFROM | LA_FLG_BINDTO;
+}
+
+#if __ELF_NATIVE_CLASS == 64
+uintptr_t
+la_symbind64 (Elf64_Sym *sym, unsigned int ndx,
+ uintptr_t *refcook, uintptr_t *defcook,
+ unsigned int *flags, const char *symname)
+#else
+uintptr_t
+la_symbind32 (Elf32_Sym *sym, unsigned int ndx,
+ uintptr_t *refcook, uintptr_t *defcook,
+ unsigned int *flags, const char *symname)
+#endif
+{
+ if (strcmp (symname, NOPLTEXIT_FUNC) == 0)
+ *flags |= LA_SYMB_NOPLTEXIT;
+ if (strcmp (symname, NOPLTENTER_FUNC) == 0)
+ *flags |= LA_SYMB_NOPLTENTER;
+ return sym->st_value;
+}
+
+ElfW(Addr)
+pltenter (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, La_regs *regs, unsigned int *flags,
+ const char *symname, long int *framesizep)
+{
+ if (strncmp (symname, PREFIX, strlen (PREFIX)) == 0)
+ {
+ fprintf (stderr, TAG ": la_pltenter: %s\n", symname);
+ *framesizep = 1024;
+ }
+ return sym->st_value;
+}
+
+unsigned int
+pltexit (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, const La_regs *inregs, La_retval *outregs,
+ const char *symname)
+{
+ if (strncmp (symname, PREFIX, strlen (PREFIX)) == 0)
+ fprintf (stderr, TAG ": la_pltexit: %s\n", symname);
+ return 0;
+}
diff --git a/elf/tst-auditmod-plt-flags-b.c b/elf/tst-auditmod-plt-flags-b.c
new file mode 100644
index 00000000000..5f1ec5c7d68
--- /dev/null
+++ b/elf/tst-auditmod-plt-flags-b.c
@@ -0,0 +1,22 @@
+/* Second auditor for tst-audit-plt-flags.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#define TAG "b"
+#define NOPLTEXIT_FUNC "tst_audit_plt_flags_func2"
+#define NOPLTENTER_FUNC ""
+#include "tst-auditmod-plt-flags-a.c"
diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile
index f046148091c..74c8fc8bada 100644
--- a/sysdeps/aarch64/Makefile
+++ b/sysdeps/aarch64/Makefile
@@ -29,6 +29,10 @@ $(objpfx)tst-audit27: $(objpfx)tst-audit27mod.so \
$(objpfx)tst-audit27mod.so: $(libsupport)
LDFLAGS-tst-audit27 += -Wl,-z,lazy
tst-audit27-ENV = LD_AUDIT=$(objpfx)tst-auditmod27.so
+
+ifeq (yes,$(have-test-bti))
+test-xfail-tst-audit-plt-flags = yes
+endif
endif
ifeq ($(subdir),elf)
diff --git a/sysdeps/alpha/Makefile b/sysdeps/alpha/Makefile
index faa59ab27a6..ebf6288ccc4 100644
--- a/sysdeps/alpha/Makefile
+++ b/sysdeps/alpha/Makefile
@@ -69,6 +69,10 @@ CFLAGS-s_isnan.c += -fno-builtin-isnanf
test-xfail-test-float32x-float64-div = yes
endif
+ifeq ($(subdir),elf)
+test-xfail-tst-audit-plt-flags = yes
+endif
+
# Build everything with full IEEE math support, and with dynamic rounding;
# there are a number of math routines that are defined to work with the
# "current" rounding mode, and it's easiest to set this with all of them.
diff --git a/sysdeps/loongarch/Makefile b/sysdeps/loongarch/Makefile
index 5818755f053..304f246630b 100644
--- a/sysdeps/loongarch/Makefile
+++ b/sysdeps/loongarch/Makefile
@@ -22,6 +22,8 @@ gen-as-const-headers += \
CFLAGS-tst-tunables-seal.c += -DTST_SEAL_TUNABLE_NAME=hwcaps
tst-tunables-seal-TUNABLES += glibc.cpu.hwcaps=-LASX
+
+test-xfail-tst-audit-plt-flags = yes
endif
ifeq ($(subdir),csu)
diff --git a/sysdeps/riscv/Makefile b/sysdeps/riscv/Makefile
index c08753ae8a3..5962c42b0eb 100644
--- a/sysdeps/riscv/Makefile
+++ b/sysdeps/riscv/Makefile
@@ -4,6 +4,8 @@ endif
ifeq ($(subdir),elf)
gen-as-const-headers += dl-link.sym
+
+test-xfail-tst-audit-plt-flags = yes
endif
# RISC-V's assembler also needs to know about PIC as it changes the definition
--
2.53.0
More information about the Libc-alpha
mailing list