[Bug libc/34503] New: powerpc: INIT_ARCH() dereferences unrelocated _rtld_global_ro, IFUNC resolvers crash under BIND_NOW

micpf at westermo dot com via Glibc-bugs <[email protected]>
Newsgroups gmane.comp.lib.glibc.bugs
Message-ID <[email protected]/bugzilla/>
https://sourceware.org/bugzilla/show_bug.cgi?id=34503

            Bug ID: 34503
           Summary: powerpc: INIT_ARCH() dereferences unrelocated
                    _rtld_global_ro, IFUNC resolvers crash under BIND_NOW
           Product: glibc
           Version: unspecified
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: libc
          Assignee: unassigned at sourceware dot org
          Reporter: micpf at westermo dot com
                CC: drepper.fsp at gmail dot com
  Target Milestone: ---

Description
===========

Since commit 21841f0d562f ("PowerPC: Influence cpu/arch hwcap features
via GLIBC_TUNABLES", first released in 2.41), the INIT_ARCH() macro in

    sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h

(also used by powerpc64 through a one-line include in
sysdeps/powerpc/powerpc64/multiarch/init-arch.h) reads hwcap and
hwcap2 via a direct

    const struct cpu_features *features = &GLRO(dl_powerpc_cpu_features);
    unsigned long int hwcap  = features->hwcap;
    unsigned long int hwcap2 = features->hwcap2;

instead of the previous __GLRO() wrapper.  __GLRO() 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.

The bug is still present on master (init-arch.h @ HEAD):

    #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;  \
      bool __attribute__((unused)) use_cached_memopt =                      \
        __GLRO(dl_powerpc_cpu_features.use_cached_memopt);                  \
      ...

Note that use_cached_memopt in the same macro still uses __GLRO(); only
hwcap and hwcap2 were converted to a direct dereference.


Impact
------

When a shared library A references an IFUNC symbol from library B and
A does not carry a DT_NEEDED on B, the dynamic linker can process A's
relocations before B's GOT is set up.  Under BIND_NOW (full RELRO) the
IFUNC resolver in B is invoked immediately as part of relocation.  With
the current INIT_ARCH(), that resolver dereferences a still-NULL
_rtld_global_ro at offset dl_powerpc_cpu_features.hwcap and segfaults.

Before 21841f0d562f the same access went through __GLRO(), whose
NULL check returned 0 in that early window; the IFUNC then simply
picked the default variant and initialisation completed correctly.


Concrete failure
----------------

Observed on PowerPC64 BE (NXP T1042 / e5500 core) with glibc 2.41 built
by OpenWrt with CONFIG_PKG_RELRO_FULL=y (BIND_NOW):

    rsyslogd (BIND_NOW binary)
      -> librsyslog
        -> libfastjson    (no DT_NEEDED on libm)
          -> modf() IFUNC resolver in libm
            -> INIT_ARCH()
              -> load of &_rtld_global_ro->dl_powerpc_cpu_features.hwcap
                 with _rtld_global_ro == NULL  ->  SIGSEGV

rsyslogd crashes immediately on startup.  Downgrading glibc to 2.38,
or applying the fix below, makes the crash disappear.

A previous attempt to work around this at the application level by
adding a "libm" dependency to libfastjson was rejected because libm is
not a separately installable package on OpenWrt; the root cause is in
glibc.

Proposed fix
------------

Restore the __GLRO()-based access for hwcap and hwcap2, matching both
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:

    --- 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); \

-- 
You are receiving this mail because:
You are on the CC list for the bug.
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.