[PATCH v4 00/14] RELRO link maps

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Mon Mar 17 17:30:09 GMT 2025



On 02/02/25 18:12, Florian Weimer wrote:
> This series introduces a new security hardening.  The main goal is to
> prevent direct overwrite attacks on the internal l_info[DT_FINI] and
> l_infi[DT_FINI_ARRAY] members of the link map.  These are attractive
> targets because the pointers are not obfuscated.  The path to DWARF data
> via _dl_find_object is kept read-only as well.
> 
> During dlopen/dlmopen and dlclose, dynamic linker data structures are
> temporarily made read-write using mprotect.  To offset the overhead from
> that, the penultimate commit in the series implements a hash table for
> dlopen.  It speeds up dlopen with many shared objects in the process,
> and makes up for the additional mprotect overhead while starting Emacs,
> for example.
> 
> The last commit optionally uses memory protection keys if available.
> POWER and x86-64 CPU support that, but the x86-64 support is not usable
> due to adverse interactions with signal handler.  The new tunable can be
> used force their use, though.  This is purely a kernel limitation; a
> different mode of operation could be selected with a pkey_alloc flag.
> POWER also has a software limitation: modern system tends to use the
> radix MMU, and protection keys are not implemented for that (only for
> the hash MMU).  Not surprisingly, with memory protection keys active, I
> could not measure any overhead (and the dlopen hash table often makes
> startup and dlopen operations a bit faster).  AArch64 protection key
> support is available in the glibc sources, but I have not been able to
> test it on an implementation.
> 
> The mprotect-based implementation is still quite slow, even with the
> speedup from the dlopen hash table.  This is very visible with the
> nptl/tst-stack4 test case: in a previous test, its run time grows from
> 3.3 seconds to to 4.3 seconds.  (It remains at 3.3 seconds if I force
> the use of memory protection keys.)  I have some ideas for speeding up
> things further, but the memprotect overhead is quite unavoidable
> unfortunately.  Things like plugin discover might be somewhat similar to
> what nptl/tst-stack4 does.  But I expect these rapid dlopen/dlclose
> sequences to be somewhat unusual, so the hardening should not have much
> of an impact in practice.

I am still not fully sold about this hardening, even with the reference
attack [1], but I also won't block if you do think this is really an
security-wise improvement. The main issue I have is the performance 
implications, specially because some projects are moving to dynamic
modules more broadly (like systemd).

The reference attack relies on both information leak and an arbitrary write, 
which themselves are quite powerful, and which makes unsure if moving to 
read-only internal data structure will really prevents such attacks taking
the thread model having arbitrary read/writes. I would expect that to fully
mitigate such issues to have some sort of CFI, although it would require more
extensive work and most likely some compiler support.

I will finish review the remaining patches, but I think the memory
protection one is not really offering much in face of current kernel
provided semantics.  Even if this is gated though through a tunable,
I think this is unlikely be acted tested or used much (maybe if dlopen
performance is an issue).

[1] https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md

> 
> The new data structures are more complex than what we had before.  The
> dl-protmem.c allocator is not protected by a fork lock (unlike malloc,
> which was used before).  This means that there is a possiblity that
> dlopen will not work after multi-threaded fork.  We sort-of support that
> as an extension (although GLRO(dl_load_write_lock) can still cause hangs
> even today, I believe).  We can probably trade memory corruption for
> hangs by careful use of GLRO(dl_load_write_lock).
> 
> I would like to add some tests for memory leaks, for example by marking
> all protected memory allocations by chasing pointers from the roots, and
> then checking that the gaps correspond exactly to the free chunks in the
> protected memory allocator.
> 
> To counteract the dlclose overhead, we could stop unloading converters
> in the iconv subsystem.  It does not seem to be a very useful thing to
> do, now that we have the dlopen hash table and many DSOs do not slow us
> down much (as long RTLD_LOCAL is used, of course).
> 
> More data structures should eventually be added under the GLPM umbrella.
> The head of the link map lists, GL(dl_ns)[nsid]._ns_loaded, is a good
> candidate for this.  These changes could happen incrementally over time.
> 
> Version 4 of this series merges some of the patches.  The first two
> patches are new.  The introduction of struct link_map_private has been
> dropped in this version, to aid with future rebases.  The optional
> debugging protected memory allocator is gone.
> 
> In the patch list below, I'm adding links to Joseph's prior reviews
> where available.
> 
>   elf: Default to ENOENT error in _dl_map_new_object
>   aarch64: Enable internal use of memory protection keys
>   Do not export <alloc_buffer.h> functions from libc
>     (merge of two patches)
>     <https://inbox.sourceware.org/libc-alpha/ed384da1-24e-7c72-5780-e3ba8b3c3ffd@redhat.com/>
>     <https://inbox.sourceware.org/libc-alpha/3d85d0a4-9a40-9046-d0a2-d7e5f0f345dd@redhat.com/>
>   support: Add <support/memprobe.h> for protection flags probing
>     <https://inbox.sourceware.org/libc-alpha/7514e96e-ca15-6728-7ea1-b22891b78cc@redhat.com/>
>   elf: Eliminate second loop in find_version in dl-version.c
>     <https://inbox.sourceware.org/libc-alpha/6e8b2e62-d72d-6218-dfb1-edd78be9cba5@redhat.com/>
>   elf: Disambiguate some failures in _dl_load_cache_lookup
>     <https://inbox.sourceware.org/libc-alpha/2115e44e-77fc-2d29-ea4-b4d3cf5a1388@redhat.com/>
>   elf: Merge the three implementations of _dl_dst_substitute
>     <https://inbox.sourceware.org/libc-alpha/1087df9e-958d-9397-8d5-908db19c6150@redhat.com/>
>   elf: Remove run-time-writable fields from struct link_map
>     (merge of four patches)
>     <https://inbox.sourceware.org/libc-alpha/f0843527-6aad-6f8b-383d-35f74455ca33@redhat.com/>
>     <https://inbox.sourceware.org/libc-alpha/724e9492-3d66-9050-922e-3213679cb16@redhat.com/>
>     <https://inbox.sourceware.org/libc-alpha/fa6ef045-377f-684a-c34b-1383314c875@redhat.com/>
>     <https://inbox.sourceware.org/libc-alpha/adeb2d8-d2cd-4f44-3062-55d4f4c88a3@redhat.com/>  
>   elf: Introduce GLPM accessor for the protected memory area
>     <https://inbox.sourceware.org/libc-alpha/9119d6b3-3fab-5070-73a4-ed699ab28052@redhat.com/>
>   elf: Bootstrap allocation for future protected memory allocator
>     <https://inbox.sourceware.org/libc-alpha/accd49e-16e-43dd-622-82c62bdd859c@redhat.com/>
>   elf: Implement a region-based protected memory allocator
>     (merge of two patches, debugging allocator removed)
>     <https://inbox.sourceware.org/libc-alpha/b270938f-76b6-113d-e1ee-c27dbe41ddce@redhat.com/>
>     <https://inbox.sourceware.org/libc-alpha/cdf9dae-2e37-8515-1c65-892743f2aabe@redhat.com/>
>   elf: Move most of the _dl_find_object data to the protected heap
>     (merge of two patches)
>     <https://inbox.sourceware.org/libc-alpha/6d5e490-f5d7-585-64b7-21c67d4369a@redhat.com/>
>     <https://inbox.sourceware.org/libc-alpha/e44a6cb3-87df-e466-849a-67ab39cb3a95@redhat.com/>
>   elf: Add hash tables to speed up DT_NEEDED, dlopen lookups
>     <https://inbox.sourceware.org/libc-alpha/7c966325-5dc7-ef9e-9da0-9cb39b8f5a68@redhat.com/>
>   elf: Use memory protection keys for the protected memory allocator
>     <https://inbox.sourceware.org/libc-alpha/886716b6-9e9b-ccba-155-e244478c3c3@redhat.com/>
> 
>  NEWS                                          |   4 +-
>  elf/Makefile                                  |  24 +
>  elf/circleload1.c                             |   3 +-
>  elf/dl-cache.c                                |  53 +-
>  elf/dl-call_fini.c                            |   2 +-
>  elf/dl-close.c                                | 140 ++---
>  elf/dl-deps.c                                 |  84 ++-
>  elf/dl-diagnostics.c                          |   2 +
>  elf/dl-dst.h                                  |  56 --
>  elf/dl-find_object.c                          | 135 ++---
>  elf/dl-find_object.h                          |   3 -
>  elf/dl-fini.c                                 |   8 +-
>  elf/dl-init.c                                 |   4 +-
>  elf/dl-libc_freeres.c                         |  20 +-
>  elf/dl-libname.c                              | 282 +++++++++
>  elf/dl-libname.h                              | 120 ++++
>  elf/dl-load.c                                 | 363 +++++-------
>  elf/dl-lookup.c                               |  42 +-
>  elf/dl-misc.c                                 |  18 -
>  elf/dl-object.c                               | 159 +++--
>  elf/dl-open.c                                 |  59 +-
>  elf/dl-origin.c                               |   3 -
>  elf/dl-protmem-internal.h                     |  66 +++
>  elf/dl-protmem.c                              | 558 ++++++++++++++++++
>  elf/dl-protmem.h                              | 102 ++++
>  elf/dl-protmem_bootstrap.h                    |  36 ++
>  elf/dl-reloc.c                                |  15 +-
>  elf/dl-sort-maps.c                            |  26 +-
>  elf/dl-static-tls.h                           |   8 +-
>  elf/dl-support.c                              |  17 +-
>  elf/dl-tls.c                                  |  37 +-
>  elf/dl-tunables.list                          |   6 +
>  elf/dl-version.c                              |  29 +-
>  elf/get-dynamic-info.h                        |   2 +-
>  elf/loadtest.c                                |   4 +-
>  elf/neededtest.c                              |   3 +-
>  elf/neededtest2.c                             |   3 +-
>  elf/neededtest3.c                             |   3 +-
>  elf/neededtest4.c                             |   3 +-
>  elf/pldd-xx.c                                 |  19 +-
>  elf/pldd.c                                    |   1 +
>  elf/rtld.c                                    | 291 +++++----
>  elf/setup-vdso.h                              |  24 +-
>  elf/sotruss-lib.c                             |   4 +-
>  elf/tst-dl-protmem.c                          | 360 +++++++++++
>  elf/tst-relro-linkmap-disabled-mod1.c         |  46 ++
>  elf/tst-relro-linkmap-disabled-mod2.c         |   2 +
>  elf/tst-relro-linkmap-disabled.c              |  64 ++
>  elf/tst-relro-linkmap-mod1.c                  |  42 ++
>  elf/tst-relro-linkmap-mod2.c                  |   2 +
>  elf/tst-relro-linkmap-mod3.c                  |   2 +
>  elf/tst-relro-linkmap.c                       | 112 ++++
>  elf/tst-rtld-dash-dash.sh                     |   2 +-
>  elf/tst-rtld-does-not-exist.sh                |   2 +-
>  elf/tst-rtld-list-tunables.exp                |   1 +
>  elf/tst-rtld-nomem.c                          | 177 ++++++
>  elf/tst-tls_tp_offset.c                       |   3 +-
>  elf/unload.c                                  |   2 +-
>  elf/unload2.c                                 |   2 +-
>  htl/pt-alloc.c                                |   5 +-
>  include/alloc_buffer.h                        |  27 +-
>  include/link.h                                | 131 ++--
>  malloc/Makefile                               |   6 +-
>  malloc/Versions                               |   7 -
>  malloc/alloc_buffer_alloc_array.c             |   1 -
>  malloc/alloc_buffer_allocate.c                |   1 -
>  malloc/alloc_buffer_copy_bytes.c              |   1 -
>  malloc/alloc_buffer_copy_string.c             |   1 -
>  malloc/alloc_buffer_create_failure.c          |   9 +-
>  malloc/tst-alloc_buffer.c                     |   4 +
>  manual/tunables.texi                          |  29 +
>  nptl/Versions                                 |   3 +-
>  nptl/pthread_create.c                         |   8 +
>  nptl_db/db_info.c                             |   1 +
>  nptl_db/structs.def                           |   3 +-
>  nptl_db/td_thr_tlsbase.c                      |  12 +-
>  nss/Makefile                                  |   4 +-
>  stdlib/cxa_thread_atexit_impl.c               |   4 +-
>  support/Makefile                              |   3 +
>  support/memprobe.h                            |  43 ++
>  support/support-alloc_buffer.c                |  26 +
>  support/support_memprobe.c                    | 251 ++++++++
>  support/tst-support_memprobe.c                | 111 ++++
>  sysdeps/aarch64/dl-machine.h                  |   5 +-
>  sysdeps/alpha/dl-machine.h                    |   4 +-
>  sysdeps/arc/dl-machine.h                      |   3 +-
>  sysdeps/arm/dl-machine.h                      |   4 +-
>  sysdeps/csky/dl-machine.h                     |   2 +-
>  sysdeps/generic/dl-early_mmap.h               |  35 ++
>  sysdeps/generic/dl-protmem-pkey.h             |  20 +
>  sysdeps/generic/ldsodefs.h                    |  97 +--
>  sysdeps/hppa/dl-machine.h                     |   3 +-
>  sysdeps/i386/dl-machine.h                     |  11 +-
>  sysdeps/loongarch/dl-tls.h                    |   2 +-
>  sysdeps/m68k/dl-tls.h                         |   2 +-
>  sysdeps/microblaze/dl-machine.h               |   3 +-
>  sysdeps/mips/Makefile                         |   6 +
>  sysdeps/mips/dl-tls.h                         |   2 +-
>  sysdeps/or1k/dl-machine.h                     |   4 +-
>  sysdeps/powerpc/dl-tls.h                      |   2 +-
>  sysdeps/powerpc/powerpc32/dl-machine.h        |   4 +-
>  sysdeps/powerpc/powerpc64/dl-machine.h        |   4 +-
>  sysdeps/riscv/dl-tls.h                        |   2 +-
>  sysdeps/s390/s390-32/dl-machine.h             |   5 +-
>  sysdeps/s390/s390-64/dl-machine.h             |   5 +-
>  sysdeps/sh/dl-machine.h                       |   7 +-
>  sysdeps/sparc/sparc32/dl-machine.h            |   4 +-
>  sysdeps/sparc/sparc64/dl-machine.h            |   4 +-
>  sysdeps/unix/sysv/linux/aarch64/pkey_get.c    |   4 +-
>  sysdeps/unix/sysv/linux/aarch64/pkey_set.c    |   4 +-
>  sysdeps/unix/sysv/linux/dl-early_allocate.c   |  17 +-
>  sysdeps/unix/sysv/linux/dl-early_mmap.h       |  41 ++
>  sysdeps/unix/sysv/linux/dl-origin.c           |   1 -
>  sysdeps/unix/sysv/linux/dl-protmem-pkey.h     |  23 +
>  sysdeps/unix/sysv/linux/dl-sysdep.c           |   2 +
>  sysdeps/unix/sysv/linux/x86/dl-protmem-pkey.h |  26 +
>  sysdeps/x86/dl-prop.h                         |   2 +-
>  sysdeps/x86_64/dl-machine.h                   |   5 +-
>  118 files changed, 3629 insertions(+), 1087 deletions(-)
>  delete mode 100644 elf/dl-dst.h
>  create mode 100644 elf/dl-libname.c
>  create mode 100644 elf/dl-libname.h
>  create mode 100644 elf/dl-protmem-internal.h
>  create mode 100644 elf/dl-protmem.c
>  create mode 100644 elf/dl-protmem.h
>  create mode 100644 elf/dl-protmem_bootstrap.h
>  create mode 100644 elf/tst-dl-protmem.c
>  create mode 100644 elf/tst-relro-linkmap-disabled-mod1.c
>  create mode 100644 elf/tst-relro-linkmap-disabled-mod2.c
>  create mode 100644 elf/tst-relro-linkmap-disabled.c
>  create mode 100644 elf/tst-relro-linkmap-mod1.c
>  create mode 100644 elf/tst-relro-linkmap-mod2.c
>  create mode 100644 elf/tst-relro-linkmap-mod3.c
>  create mode 100644 elf/tst-relro-linkmap.c
>  create mode 100644 elf/tst-rtld-nomem.c
>  create mode 100644 support/memprobe.h
>  create mode 100644 support/support-alloc_buffer.c
>  create mode 100644 support/support_memprobe.c
>  create mode 100644 support/tst-support_memprobe.c
>  create mode 100644 sysdeps/generic/dl-early_mmap.h
>  create mode 100644 sysdeps/generic/dl-protmem-pkey.h
>  create mode 100644 sysdeps/unix/sysv/linux/dl-early_mmap.h
>  create mode 100644 sysdeps/unix/sysv/linux/dl-protmem-pkey.h
>  create mode 100644 sysdeps/unix/sysv/linux/x86/dl-protmem-pkey.h
> 
> 
> base-commit: fc058b46c78f090580c1ca5b785b54ae4092f0c1



More information about the Libc-alpha mailing list