mbox series

[v4,0/9] Add support for memory sealing

Message ID 20241206173850.3766841-1-adhemerval.zanella@linaro.org
Headers show
Series Add support for memory sealing | expand

Message

Adhemerval Zanella Dec. 6, 2024, 5:37 p.m. UTC
The Linux 6.10 (8be7258aad44b5e25977a98db136f677fa6f4370) added the
mseal syscall that allows blocking some memory operations on the VMA
range:

 * Unmapping, moving to another location, extending or shrinking the
   size, munmap, and mremap.
 * Moving or expanding a different VMA into the current location, via
   mremap.
 * Modifying the memory range with mmap along with flag MAP_FIXED.
 * Expanding the size with mremap.
 * Change the protection flags with mprotect or pkey_mprotect.
 * Destructive behaviors on anonymous memory, such as madvice with
   MADV_DONTNEED.

Memory sealing is useful as a hardening mechanism [1] to avoid either
remapping the memory segments or changing the memory protection segments
layout by the dynamic loader (for instance, the RELRO hardening). A
similar hardening is done by OpenBSD with the mimmutable syscall [2].

The sealing is an opt-in security feature that requires a new GNU
property GNU_PROPERTY_MEMORY_SEAL to indicate that the ELF module
supports and should use memory sealing if the loader supports it [3].
Previous versions had the sealing as an opt-out feature, however, it
has some drawbacks where the backport is not straightforward, there is
no clear semantic if memory sealing is a hint or requirement, some
programs bypass the loader to apply relocation themselves and are
incompatible with an opt-out feature [4], and it deviates from how other
security hardening was added on Linux ecosystem (such as RELRO and
non-executable stacks).

A GNU property is used instead of a new dynamic section tag (like the
one proposed for DT_GNU_FLAGS_1) because the memory sealing should be
selectable for ET_EXEC and not only for ET_DYN. It also fits new opt-in
security features like x86 CET or AArch64 BTI. 

The first patch adds the mseal support for Linux.  Although most
programs will not use it directly, some specific ones, like Chrome,
intend to use it.

The second and third patches are requirements to enable memory sealing
to work on executables, where they add gnu property parsing on the
loader and static binaries.

The fourth patch moves 'call_init_paths' after gnu attribute parsing, so
the loader can seal the rtld_malloc pages (since they are meant to be
immutable over process execution).

The fifth patch propagates the RTLD_NODELETE flag in case of dlopen. It
will be used to extend memory sealing for the object dependencies.

The sixth patch adds the memory sealing supports in multiple places
where the page is supposed to be immutable over program execution:
 * All shared library dependencies from the binary, including the
   read-only segments after PT_GNU_RELRO setup.
 * The binary itself, including dynamic and static links. In both cases,
   it is up either to binary or the loader to set up the sealing.
 * Any preload libraries.
 * Any library loaded with dlopen with RTLD_NODELETE flag (including
   libgcc.so loaded to enable process unwind and thread cancellation).
 * Audit modules.
 * The loader bump allocator.

The seventh patch makes glibc enable memory sealing as default if the
linker supports the option (-Wl,memory-seal). A new configure option,
--disable-default-memory-seal, disable it.

The eighth patch adds memory sealing tests, they are enabled if the
linker supports it.

The last patch adds a new tunable, glibc.rtld.seal, which can be used to
enforce memory sealing even if the programs or dependencies do not have
the GNU_PROPERTY_MEMORY_SEAL. The tunable accepts two different values:

* '0': where loaders follow the GNU_PROPERTY_MEMORY_SEAL attribute if
* present.  This is the default and no sealing would be applied if the
* object does not have the memory sealing attribute.

* '1': where sealing is enforced even if the object does not have the
* GNU_PROPERTY_MEMORY_SEAL.  Also, any syscall failure on memory sealing
* aborts the programs.

This patchset does not delay RELRO activation until after their ELF
constructors have been executed, as suggested on the previous RFC for
mseal support. It is not strictly required, and it requires extensive
changes on_dl_start_user to either make _dl_init call RELRO/sealing
setup after ctor/initarray is done, or call it after _dl_init.  There 
is also the question of whether to apply RELRO/sealing per module after
dtor/initarray or in bulk after _dt_init.

I tested on both x86_64-linux-gnu and aarch64-linux-gnu with Linux
6.12, along with some testing on a powerpc64le-linux-gnu VM.

[1] https://blog.trailofbits.com/2024/10/25/a-deep-dive-into-linuxs-new-mseal-syscall/
[2] https://man.openbsd.org/mimmutable.2
[3] https://sourceware.org/pipermail/binutils/2024-November/137867.html
[4] https://glandium.org/blog/?p=4297

Changes v3->v4:
* Rebase against master (remove nios2 ABI update and handle f2326c2ec0a0a8db
  changes).
* Handle vvar_vclock mapping on tests.
 
Changes v2->v3:
* Make the option opt-int instead of opt-out.

Adhemerval Zanella (9):
  linux: Add mseal syscall support
  elf: Parse gnu properties for static linked binaries
  elf: Parse gnu properties for the loader
  rtld: Move call_init_paths after _dl_process_pt_gnu_property
  elf: Use RTLD_NODELETE for dependencies
  elf: Add support to memory sealing
  Enable memory sealing automatically
  linux: Add memory sealing tests
  elf: Add glibc.rtld.seal tunable

 INSTALL                                       |   5 +
 Makeconfig                                    |  17 ++
 Makerules                                     |   2 +
 NEWS                                          |  20 ++
 configure                                     |  57 ++++
 configure.ac                                  |  19 ++
 elf/Makefile                                  |   1 +
 elf/dl-load.c                                 |   7 +
 elf/dl-map-segments.h                         |  16 +-
 elf/dl-minimal-malloc.c                       |   3 +
 elf/dl-mseal-mode.h                           |  28 ++
 elf/dl-open.c                                 |   7 +-
 elf/dl-reloc.c                                |  63 ++++
 elf/dl-support.c                              |  18 ++
 elf/dl-tunables.list                          |   6 +
 elf/elf.h                                     |   2 +
 elf/rtld.c                                    |  31 +-
 elf/setup-vdso.h                              |   2 +
 elf/tst-rtld-list-tunables.exp                |   1 +
 include/link.h                                |   8 +
 manual/install.texi                           |   5 +
 manual/memory.texi                            |  66 +++++
 manual/tunables.texi                          |  35 +++
 sysdeps/aarch64/dl-prop.h                     |   5 +
 sysdeps/generic/dl-mseal.h                    |  23 ++
 sysdeps/generic/dl-prop-mseal.h               |  36 +++
 sysdeps/generic/dl-prop.h                     |   5 +
 sysdeps/generic/ldsodefs.h                    |  15 +
 sysdeps/unix/sysv/linux/Makefile              | 107 +++++++
 sysdeps/unix/sysv/linux/Versions              |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/bits/mman-shared.h    |   8 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/dl-mseal.c            |  48 +++
 sysdeps/unix/sysv/linux/dl-mseal.h            |  27 ++
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/kernel-features.h     |   8 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |   1 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/syscalls.list         |   1 +
 .../sysv/linux/tst-dl_mseal-auditmod-noseal.c |   1 +
 .../unix/sysv/linux/tst-dl_mseal-auditmod.c   |  23 ++
 .../unix/sysv/linux/tst-dl_mseal-dlopen-1-1.c |  19 ++
 .../unix/sysv/linux/tst-dl_mseal-dlopen-1.c   |  19 ++
 .../linux/tst-dl_mseal-dlopen-2-1-noseal.c    |  19 ++
 .../unix/sysv/linux/tst-dl_mseal-dlopen-2-1.c |  19 ++
 .../sysv/linux/tst-dl_mseal-dlopen-2-noseal.c |  19 ++
 .../unix/sysv/linux/tst-dl_mseal-dlopen-2.c   |  19 ++
 .../sysv/linux/tst-dl_mseal-mod-1-noseal.c    |  19 ++
 sysdeps/unix/sysv/linux/tst-dl_mseal-mod-1.c  |  19 ++
 .../sysv/linux/tst-dl_mseal-mod-2-noseal.c    |  19 ++
 sysdeps/unix/sysv/linux/tst-dl_mseal-mod-2.c  |  19 ++
 sysdeps/unix/sysv/linux/tst-dl_mseal-noseal.c |  74 +++++
 .../sysv/linux/tst-dl_mseal-preload-noseal.c  |   1 +
 .../unix/sysv/linux/tst-dl_mseal-preload.c    |  19 ++
 .../unix/sysv/linux/tst-dl_mseal-skeleton.c   | 279 ++++++++++++++++++
 .../sysv/linux/tst-dl_mseal-static-noseal.c   |  45 +++
 sysdeps/unix/sysv/linux/tst-dl_mseal-static.c |  42 +++
 .../unix/sysv/linux/tst-dl_mseal-tunable.c    |  76 +++++
 sysdeps/unix/sysv/linux/tst-dl_mseal.c        |  72 +++++
 sysdeps/unix/sysv/linux/tst-mseal.c           |  67 +++++
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 sysdeps/x86/dl-prop.h                         |   4 +
 88 files changed, 1616 insertions(+), 11 deletions(-)
 create mode 100644 elf/dl-mseal-mode.h
 create mode 100644 sysdeps/generic/dl-mseal.h
 create mode 100644 sysdeps/generic/dl-prop-mseal.h
 create mode 100644 sysdeps/unix/sysv/linux/dl-mseal.c
 create mode 100644 sysdeps/unix/sysv/linux/dl-mseal.h
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-auditmod-noseal.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-auditmod.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-dlopen-1-1.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-dlopen-1.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-dlopen-2-1-noseal.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-dlopen-2-1.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-dlopen-2-noseal.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-dlopen-2.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-mod-1-noseal.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-mod-1.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-mod-2-noseal.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-mod-2.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-noseal.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-preload-noseal.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-preload.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-skeleton.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-static-noseal.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-static.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-tunable.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal.c
 create mode 100644 sysdeps/unix/sysv/linux/tst-mseal.c