Re: [PATCH] powerpc: Restore NULL check on _rtld_global_ro in INIT_ARCH [BZ #34503]

Michael Pfeifroth <[email protected]>
Newsgroups gmane.comp.lib.glibc.alpha
Message-ID <[email protected]>
On 11/08/26 08:50, Adhemerval Zanella Netto wrote:
> The patch looks good, but maybe it would be good to add a regression testcase
> for that.  [...]

Thanks for the review.  v2 adds the test as suggested — ppc64 only as
you noted.

The module (tst-ppc64-ifunc-bind-now-mod.so) is linked with -z,now and
has no DT_NEEDED on libm; the main binary has DT_NEEDED on libm so modf
resolves correctly at runtime.  The test simply calls ifunc_ptr(2.5,
&ip) and checks the result: without the fix the process would segfault
before reaching the check.

-- >8 --
From 0000000000000000000000000000000000000002 Mon Sep 17 00:00:00 2001
From: Michael Pfeifroth <[email protected]>
Date: Mon, 11 Aug 2026 14:30:00 +0200
Subject: [PATCH v2] powerpc: Restore NULL check on _rtld_global_ro in
 INIT_ARCH [BZ #34503]

Commit 21841f0d562f ("PowerPC: Influence cpu/arch hwcap features via
GLIBC_TUNABLES") changed the INIT_ARCH() macro used by powerpc32/power4
and (via a one-line include) powerpc64 multiarch IFUNC resolvers to
read hwcap and hwcap2 through a direct

    &GLRO(dl_powerpc_cpu_features)

reference, instead of the previous __GLRO() wrapper.  The __GLRO() macro
performs a volatile NULL check on _rtld_global_ro, which matters because
IFUNC resolvers can run before _rtld_global_ro has been relocated for the
current library.

This regression triggers when a shared library's IFUNC symbol from libm
is resolved via BIND_NOW (full RELRO) before libm's own GOT is relocated:
the resolver's INIT_ARCH() then dereferences a NULL _rtld_global_ro and
segfaults at the hwcap load.  The concrete failure seen was rsyslogd
crashing on startup on powerpc64 (e5500, BE) with

    rsyslogd -> librsyslog -> libfastjson -> modf() IFUNC in libm

when libfastjson lacked a DT_NEEDED on libm, so libm was relocated after
libfastjson's IFUNC resolvers ran.

Restore the __GLRO()-based access for both hwcap and hwcap2, matching
the pre-2.41 behaviour and how use_cached_memopt is already read in the
same macro.  This is a no-op once _rtld_global_ro is fully initialised
and simply reinstates the early-startup NULL guard.

Add a regression test (ppc64 only; ppc32 has additional early-startup
constraints that make the same test infeasible there).  The module is
linked with -z,now and intentionally has no DT_NEEDED on libm, so the
IFUNC resolver for modf() runs before libm is fully relocated.

Signed-off-by: Michael Pfeifroth <[email protected]>
---
 sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h      |  6 ++--
 sysdeps/powerpc/powerpc64/Makefile                          | 13 +++++++
 sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now-mod.c   | 28 +++++++++++++++
 sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now.c       | 45 +++++++++++++++++++++++++
 4 files changed, 89 insertions(+), 3 deletions(-)
 create mode 100644 sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now-mod.c
 create mode 100644 sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now.c

diff --git a/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h b/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
index 9aab67a5..e681b452 100644
--- a/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
+++ b/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
@@ -36,9 +36,9 @@
 /* Get the hardware information post the tunables set, the macro checks
    it and fills the previous ones.  */
 #define INIT_ARCH() \
-  const struct cpu_features *features = &GLRO(dl_powerpc_cpu_features);	\
-  unsigned long int hwcap = features->hwcap;				\
-  unsigned long int __attribute__((unused)) hwcap2 = features->hwcap2; \
+  unsigned long int hwcap = __GLRO(dl_powerpc_cpu_features.hwcap);	\
+  unsigned long int __attribute__((unused)) hwcap2 =			\
+    __GLRO(dl_powerpc_cpu_features.hwcap2);				\
   bool __attribute__((unused)) use_cached_memopt =		\
     __GLRO(dl_powerpc_cpu_features.use_cached_memopt);		\
   if (hwcap & PPC_FEATURE_ARCH_2_06)				\

diff --git a/sysdeps/powerpc/powerpc64/Makefile b/sysdeps/powerpc/powerpc64/Makefile
index 12345678..abcdefab 100644
--- a/sysdeps/powerpc/powerpc64/Makefile
+++ b/sysdeps/powerpc/powerpc64/Makefile
@@ -42,6 +42,19 @@ CFLAGS-rtld-strnlen.os = $(no-special-regs)
 ifeq ($(subdir),elf)
 # help gcc inline asm code from dl-machine.h
 +cflags += -finline-limit=2000
+
+# Regression test for BZ #34503: IFUNC resolver crash with BIND_NOW when
+# _rtld_global_ro has not been relocated yet in the calling library's GOT.
+# Restricted to ppc64; ppc32 has additional early-startup constraints.
+tests += tst-ppc64-ifunc-bind-now
+modules-names += tst-ppc64-ifunc-bind-now-mod
+tst-ppc64-ifunc-bind-now-mod.so-no-z-defs = yes
+LDFLAGS-tst-ppc64-ifunc-bind-now-mod.so = -Wl,-z,now
+$(objpfx)tst-ppc64-ifunc-bind-now: $(objpfx)tst-ppc64-ifunc-bind-now-mod.so
+LDLIBS-tst-ppc64-ifunc-bind-now = -Wl,--no-as-needed -lm \
+  $(objpfx)tst-ppc64-ifunc-bind-now-mod.so
 endif

diff --git a/sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now-mod.c b/sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now-mod.c
new file mode 100644
index 00000000..11111111
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now-mod.c
@@ -0,0 +1,28 @@
+/* Shared library module for tst-ppc64-ifunc-bind-now.
+   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/>.  */
+
+/* Reference modf without a DT_NEEDED on libm.  This module is linked
+   with -z,now so its BIND_NOW IFUNC relocation for modf() runs as early
+   as possible during dynamic linking -- before _rtld_global_ro has been
+   relocated in this library's GOT context.  Tests that INIT_ARCH()
+   handles a NULL _rtld_global_ro gracefully (BZ #34503).  */
+
+extern double modf (double, double *);
+
+double (*volatile ifunc_ptr) (double, double *) = modf;

diff --git a/sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now.c b/sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now.c
new file mode 100644
index 00000000..22222222
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now.c
@@ -0,0 +1,45 @@
+/* Regression test for BZ #34503: powerpc IFUNC resolver crash with BIND_NOW.
+   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/>.  */
+
+/* Verify that calling an IFUNC function (modf from libm) via a function
+   pointer exported from a shared library that was linked with -z,now and
+   has no DT_NEEDED on libm does not crash.
+
+   Without the fix, INIT_ARCH() in the modf IFUNC resolver dereferences
+   _rtld_global_ro directly; if that pointer has not been relocated yet
+   in the module's GOT context, the dereference faults.  With the fix,
+   the __GLRO() wrapper returns 0 when _rtld_global_ro is NULL and the
+   resolver falls back to the default implementation.  */
+
+#include <math.h>
+#include <stdio.h>
+#include <support/check.h>
+
+/* Exported by tst-ppc64-ifunc-bind-now-mod.so, which is built with
+   -z,now and has no DT_NEEDED on libm.  */
+extern double (*volatile ifunc_ptr) (double, double *);
+
+static int
+do_test (void)
+{
+  double ip;
+  double frac = ifunc_ptr (2.5, &ip);
+  printf ("modf(2.5) = %g + %g (expect 0.5 + 2)\n", frac, ip);
+  TEST_VERIFY (frac == 0.5);
+  TEST_VERIFY (ip == 2.0);
+  return 0;
+}
+
+#include <support/test-driver.c>
-- 
2.34.1

On 8/10/26 12:41, Michael Pfeifroth wrote:
> Sorry, v1 was mangled by my mail client (tabs converted to spaces on
> paste from X11 primary selection).  Resending correctly.  No changes
> from v1 otherwise.
> 
> -- >8 --
> Commit 21841f0d562f ("PowerPC: Influence cpu/arch hwcap features via
> GLIBC_TUNABLES") changed the INIT_ARCH() macro used by powerpc32/power4
> and (via a one-line include) powerpc64 multiarch IFUNC resolvers to
> read hwcap and hwcap2 through a direct
> 
>     &GLRO(dl_powerpc_cpu_features)
> 
> reference, instead of the previous __GLRO() wrapper.  The __GLRO() macro
> performs a volatile NULL check on _rtld_global_ro, which matters because
> IFUNC resolvers can run before _rtld_global_ro has been relocated for the
> current library.
> 
> This regression triggers when a shared library's IFUNC symbol from libm
> is resolved via BIND_NOW (full RELRO) before libm's own GOT is relocated:
> the resolver's INIT_ARCH() then dereferences a NULL _rtld_global_ro and
> segfaults at the hwcap load.  The concrete failure seen was rsyslogd
> crashing on startup on powerpc64 (e5500, BE) with
> 
>     rsyslogd -> librsyslog -> libfastjson -> modf() IFUNC in libm
> 
> when libfastjson lacked a DT_NEEDED on libm, so libm was relocated after
> libfastjson's IFUNC resolvers ran.
> 
> Restore the __GLRO()-based access for both hwcap and hwcap2, matching
> the pre-2.41 behaviour and how use_cached_memopt is already read in the
> same macro.  This is a no-op once _rtld_global_ro is fully initialised
> and simply reinstates the early-startup NULL guard.
> 
> Signed-off-by: Michael Pfeifroth <[email protected]>
> ---
>  sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h b/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
> index 9aab67a5..e681b452 100644
> --- a/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
> +++ b/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
> @@ -36,9 +36,9 @@
>  /* Get the hardware information post the tunables set, the macro checks
>     it and fills the previous ones.  */
>  #define INIT_ARCH() \
> -  const struct cpu_features *features = &GLRO(dl_powerpc_cpu_features);	\
> -  unsigned long int hwcap = features->hwcap;				\
> -  unsigned long int __attribute__((unused)) hwcap2 = features->hwcap2; \
> +  unsigned long int hwcap = __GLRO(dl_powerpc_cpu_features.hwcap);	\
> +  unsigned long int __attribute__((unused)) hwcap2 =			\
> +    __GLRO(dl_powerpc_cpu_features.hwcap2);				\
>    bool __attribute__((unused)) use_cached_memopt =		\
>      __GLRO(dl_powerpc_cpu_features.use_cached_memopt);		\
>    if (hwcap & PPC_FEATURE_ARCH_2_06)				\
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.