public inbox for glibc-cvs@sourceware.org
help / color / mirror / Atom feed
* [glibc/azanella/mseal] elf: Add support to memory sealing
@ 2024-06-04 14:08 Adhemerval Zanella
  0 siblings, 0 replies; 5+ messages in thread
From: Adhemerval Zanella @ 2024-06-04 14:08 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=d32da241411f932e8dccf723f7fc547cfd6b6850

commit d32da241411f932e8dccf723f7fc547cfd6b6850
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Sun Jun 2 20:31:27 2024 -0300

    elf: Add support to memory sealing
    
    It uses the new Linux mseal syscall to seal memory mappings to avoid
    further changes (such changing the memory protection or remap).  The
    sealing is done in multiple places where the memory is supported to
    be immuatable over program execution:
    
      * All DT_NEEDED dependencies from the binary, including the RELRO
        seguments after PT_GNU_RELRO setup.
    
      * The binary itself, including dynamic and static linked.  In both
        cases it is up either to binary or the loader to setup the
        sealing.
    
      * The ld.so.cache cache.
    
      * The vDSO segment (if existent).
    
      * Any preload libraries.
    
    For binary dependencies, the RTLD_NODELETE is used to signal the
    link_map should be sealed.  It also make dlopen objects with the
    flag sealed as well.
    
    The sealing is controlled by a new tunable, glibc.rtld.seal, with
    three different states:
    
      0. Disabled  where no sealing is done.  This is the default.
    
      1. Enabled, where loader will issue the mseal syscall on the
         memory mappings but any failure will be ignored.
    
      2. Enforce, similar to Enabled but any failure from the mseal
         will terminate the process.
    
    Checked on x86_64-linux-gnu.

Diff:
---
 elf/dl-cache.c                     |  3 +++
 elf/dl-load.c                      |  2 ++
 elf/dl-mseal-mode.h                | 29 ++++++++++++++++++++++
 elf/dl-reloc.c                     | 33 ++++++++++++++++++++++++
 elf/dl-support.c                   |  7 ++++++
 elf/dl-tunables.list               |  5 ++++
 elf/rtld.c                         | 10 +++++---
 elf/setup-vdso.h                   |  3 +++
 elf/tst-rtld-list-tunables.exp     |  1 +
 include/link.h                     |  1 +
 string/strerrorname_np.c           |  1 +
 sysdeps/generic/dl-mseal.h         | 25 +++++++++++++++++++
 sysdeps/generic/ldsodefs.h         |  4 +++
 sysdeps/unix/sysv/linux/Makefile   |  4 +++
 sysdeps/unix/sysv/linux/dl-mseal.c | 51 ++++++++++++++++++++++++++++++++++++++
 sysdeps/unix/sysv/linux/dl-mseal.h | 29 ++++++++++++++++++++++
 16 files changed, 205 insertions(+), 3 deletions(-)

diff --git a/elf/dl-cache.c b/elf/dl-cache.c
index 85f3f179ed..1770c90194 100644
--- a/elf/dl-cache.c
+++ b/elf/dl-cache.c
@@ -26,6 +26,7 @@
 #include <_itoa.h>
 #include <dl-hwcaps.h>
 #include <dl-isa-level.h>
+#include <dl-mseal.h>
 
 #ifndef _DL_PLATFORMS_COUNT
 # define _DL_PLATFORMS_COUNT 0
@@ -410,6 +411,8 @@ _dl_load_cache_lookup (const char *name)
       /* Read the contents of the file.  */
       void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,
 					       PROT_READ);
+      if (cache != MAP_FAILED)
+	_dl_mseal (file, cachesize);
 
       /* We can handle three different cache file formats here:
 	 - only the new format
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 8a89b71016..7d8824ea89 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1431,6 +1431,8 @@ cannot enable executable stack as shared object requires");
     /* Assign the next available module ID.  */
     _dl_assign_tls_modid (l);
 
+  l->l_seal = mode & RTLD_NODELETE ? 1 : 0;
+
 #ifdef DL_AFTER_LOAD
   DL_AFTER_LOAD (l);
 #endif
diff --git a/elf/dl-mseal-mode.h b/elf/dl-mseal-mode.h
new file mode 100644
index 0000000000..7f9ede4db7
--- /dev/null
+++ b/elf/dl-mseal-mode.h
@@ -0,0 +1,29 @@
+/* Memory sealing.  Generic definitions.
+   Copyright (C) 2024 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/>.  */
+
+#ifndef _DL_SEAL_MODE_H
+#define _DL_SEAL_MODE_H
+
+enum dl_seal_mode
+{
+  DL_SEAL_DISABLE = 0,
+  DL_SEAL_ENABLE = 1,
+  DL_SEAL_ENFORCE = 2,
+};
+
+#endif
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 4bf7aec88b..f0bde68782 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -28,6 +28,7 @@
 #include <_itoa.h>
 #include <libc-pointer-arith.h>
 #include "dynamic-link.h"
+#include <dl-mseal.h>
 
 /* Statistics function.  */
 #ifdef SHARED
@@ -347,6 +348,11 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
      done, do it.  */
   if (l->l_relro_size != 0)
     _dl_protect_relro (l);
+
+  /* Seal the memory mapping after RELRO setup, we can use the PT_LOAD
+     segments because even if relro splits the the original RW VMA,
+     mseal works with multiple VMAs with different flags.  */
+  _dl_mseal_map (l);
 }
 
 
@@ -369,6 +375,33 @@ cannot apply additional memory protection after relocation");
     }
 }
 
+void
+_dl_mseal_map (const struct link_map *l)
+{
+  if (l->l_seal)
+    {
+      if (l->l_contiguous)
+	_dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
+      else
+	{
+	  for (const ElfW(Phdr) *ph = l->l_phdr;
+	       ph < &l->l_phdr[l->l_phnum];
+	       ++ph)
+	    switch (ph->p_type)
+	      {
+	      case PT_LOAD:
+		{
+		  ElfW(Addr) mapstart = l->l_addr
+		      + (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1));
+		  ElfW(Addr) allocend = l->l_addr + ph->p_vaddr + ph->p_memsz;
+		  _dl_mseal ((void *) mapstart, allocend - mapstart);
+		}
+	        break;
+	    }
+	}
+    }
+}
+
 void
 __attribute_noinline__
 _dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt)
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 451932dd03..a206049ce3 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -45,6 +45,7 @@
 #include <dl-find_object.h>
 #include <array_length.h>
 #include <dl-symbol-redir-ifunc.h>
+#include <dl-mseal.h>
 
 extern char *__progname;
 char **_dl_argv = &__progname;	/* This is checked for some error messages.  */
@@ -99,6 +100,7 @@ static struct link_map _dl_main_map =
     .l_used = 1,
     .l_tls_offset = NO_TLS_OFFSET,
     .l_serial = 1,
+    .l_seal = SUPPORT_MSEAL,
   };
 
 /* Namespace information.  */
@@ -340,6 +342,11 @@ _dl_non_dynamic_init (void)
   /* Setup relro on the binary itself.  */
   if (_dl_main_map.l_relro_size != 0)
     _dl_protect_relro (&_dl_main_map);
+
+  /* Seal the memory mapping after RELRO setup, we can use the PT_LOAD
+     segments because even if relro splits the the original RW VMA,
+     mseal works with multiple VMAs with different flags.  */
+  _dl_mseal_map (&_dl_main_map);
 }
 
 #ifdef DL_SYSINFO_IMPLEMENTATION
diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index 1186272c81..ba72b3152d 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -142,6 +142,11 @@ glibc {
       maxval: 1
       default: 0
     }
+    seal {
+      type: INT_32
+      minval: 0
+      maxval: 2
+    }
   }
 
   mem {
diff --git a/elf/rtld.c b/elf/rtld.c
index e9525ea987..174389e205 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -53,6 +53,7 @@
 #include <dl-find_object.h>
 #include <dl-audit-check.h>
 #include <dl-call_tls_init_tp.h>
+#include <dl-mseal.h>
 
 #include <assert.h>
 
@@ -477,6 +478,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
   GL(dl_rtld_map).l_real = &GL(dl_rtld_map);
   GL(dl_rtld_map).l_map_start = (ElfW(Addr)) &__ehdr_start;
   GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;
+  GL(dl_rtld_map).l_seal = 1;
   /* Copy the TLS related data if necessary.  */
 #ifndef DONT_USE_BOOTSTRAP_MAP
 # if NO_TLS_OFFSET != 0
@@ -809,7 +811,8 @@ do_preload (const char *fname, struct link_map *main_map, const char *where)
 
   args.str = fname;
   args.loader = main_map;
-  args.mode = __RTLD_SECURE;
+  /* RTLD_NODELETE enables sealing.  */
+  args.mode = __RTLD_SECURE | RTLD_NODELETE;
 
   unsigned int old_nloaded = GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
 
@@ -1123,6 +1126,7 @@ rtld_setup_main_map (struct link_map *main_map)
   /* And it was opened directly.  */
   ++main_map->l_direct_opencount;
   main_map->l_contiguous = 1;
+  main_map->l_seal = 1;
 
   /* A PT_LOAD segment at an unexpected address will clear the
      l_contiguous flag.  The ELF specification says that PT_LOAD
@@ -1636,7 +1640,7 @@ dl_main (const ElfW(Phdr) *phdr,
       /* Create a link_map for the executable itself.
 	 This will be what dlopen on "" returns.  */
       main_map = _dl_new_object ((char *) "", "", lt_executable, NULL,
-				 __RTLD_OPENEXEC, LM_ID_BASE);
+				 __RTLD_OPENEXEC | RTLD_NODELETE, LM_ID_BASE);
       assert (main_map != NULL);
       main_map->l_phdr = phdr;
       main_map->l_phnum = phnum;
@@ -1964,7 +1968,7 @@ dl_main (const ElfW(Phdr) *phdr,
     RTLD_TIMING_VAR (start);
     rtld_timer_start (&start);
     _dl_map_object_deps (main_map, preloads, npreloads,
-			 state.mode == rtld_mode_trace, 0);
+			 state.mode == rtld_mode_trace, RTLD_NODELETE);
     rtld_timer_accum (&load_time, start);
   }
 
diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h
index 888e1e4897..fe5b1928c3 100644
--- a/elf/setup-vdso.h
+++ b/elf/setup-vdso.h
@@ -66,6 +66,7 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
 
       /* The vDSO is always used.  */
       l->l_used = 1;
+      l->l_seal = 1;
 
       /* Initialize l_local_scope to contain just this map.  This allows
 	 the use of dl_lookup_symbol_x to resolve symbols within the vdso.
@@ -104,6 +105,8 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
       if (GLRO(dl_sysinfo) == DL_SYSINFO_DEFAULT)
 	GLRO(dl_sysinfo) = GLRO(dl_sysinfo_dso)->e_entry + l->l_addr;
 # endif
+
+      _dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
     }
 #endif
 }
diff --git a/elf/tst-rtld-list-tunables.exp b/elf/tst-rtld-list-tunables.exp
index db0e1c86e9..6f1044c480 100644
--- a/elf/tst-rtld-list-tunables.exp
+++ b/elf/tst-rtld-list-tunables.exp
@@ -15,3 +15,4 @@ glibc.rtld.dynamic_sort: 2 (min: 1, max: 2)
 glibc.rtld.enable_secure: 0 (min: 0, max: 1)
 glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
 glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0x[f]+)
+glibc.rtld.seal: 0 (min: 0, max: 2)
diff --git a/include/link.h b/include/link.h
index cb0d7d8e2f..b7c19f61da 100644
--- a/include/link.h
+++ b/include/link.h
@@ -212,6 +212,7 @@ struct link_map
     unsigned int l_find_object_processed:1; /* Zero if _dl_find_object_update
 					       needs to process this
 					       lt_library map.  */
+    unsigned int l_seal:1;
 
     /* NODELETE status of the map.  Only valid for maps of type
        lt_loaded.  Lazy binding sets l_nodelete_active directly,
diff --git a/string/strerrorname_np.c b/string/strerrorname_np.c
index 042cea381c..e0e22fa79e 100644
--- a/string/strerrorname_np.c
+++ b/string/strerrorname_np.c
@@ -17,6 +17,7 @@
    <https://www.gnu.org/licenses/>.  */
 
 #include <stdio.h>
+#include <string.h>
 
 const char *
 strerrorname_np (int errnum)
diff --git a/sysdeps/generic/dl-mseal.h b/sysdeps/generic/dl-mseal.h
new file mode 100644
index 0000000000..a5399e4d5c
--- /dev/null
+++ b/sysdeps/generic/dl-mseal.h
@@ -0,0 +1,25 @@
+/* Memory sealing.  Generic version.
+   Copyright (C) 2024 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/>.  */
+
+static inline int
+_dl_mseal (void *addr, size_t len)
+{
+  return 0;
+}
+
+#define SUPPORT_MSEAL 0
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 50f58a60e3..7ad747acf4 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1017,6 +1017,10 @@ extern void _dl_relocate_object (struct link_map *map,
 /* Protect PT_GNU_RELRO area.  */
 extern void _dl_protect_relro (struct link_map *map) attribute_hidden;
 
+/* Scan all PT_LOAD entries and seal the VMA range to avoid further
+   changes.  */
+extern void _dl_mseal_map (const struct link_map *map) attribute_hidden;
+
 /* Call _dl_signal_error with a message about an unhandled reloc type.
    TYPE is the result of ELFW(R_TYPE) (r_info), i.e. an R_<CPU>_* value.
    PLT is nonzero if this was a PLT reloc; it just affects the message.  */
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 82d523e588..8c181d46dc 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -625,6 +625,10 @@ sysdep-rtld-routines += \
   dl-sbrk \
   # sysdep-rtld-routines
 
+dl-routines += \
+  dl-mseal \
+  # dl-routines
+
 others += \
   pldd \
   # others
diff --git a/sysdeps/unix/sysv/linux/dl-mseal.c b/sysdeps/unix/sysv/linux/dl-mseal.c
new file mode 100644
index 0000000000..1313fa3868
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.c
@@ -0,0 +1,51 @@
+/* Memory sealing.  Linux version.
+   Copyright (C) 2024 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 <atomic.h>
+#include <dl-mseal.h>
+#include <dl-mseal-mode.h>
+#include <dl-tunables.h>
+#include <ldsodefs.h>
+
+int
+_dl_mseal (void *addr, size_t len)
+{
+  int32_t mode = TUNABLE_GET (glibc, rtld, seal, int32_t, NULL);
+  if (mode == DL_SEAL_DISABLE)
+    return 0;
+
+  int r;
+#if __ASSUME_MSEAL
+  r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0);
+#else
+  r = -ENOSYS;
+  static int mseal_supported = true;
+  if (atomic_load_relaxed (&mseal_supported))
+    {
+      r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0);
+      if (r == -ENOSYS)
+	atomic_store_relaxed (&mseal_supported, false);
+    }
+#endif
+  if (mode == DL_SEAL_ENFORCE && r != 0)
+    _dl_fatal_printf ("Fatal error: sealing is enforced and kernel has "
+		      "failed for 0x%lx-0x%lx range\n",
+		      (long unsigned int) addr,
+		      (long unsigned int) addr + len);
+  return r;
+}
diff --git a/sysdeps/unix/sysv/linux/dl-mseal.h b/sysdeps/unix/sysv/linux/dl-mseal.h
new file mode 100644
index 0000000000..f56d33d97c
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.h
@@ -0,0 +1,29 @@
+/* Memory sealing.  Linux version.
+   Copyright (C) 2024 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/>.  */
+
+/* Seal the ADDR or size LEN to protect against modifications, such as
+   changes on the permission flags (through mprotect), remap (through
+   mmap and/or remap), shrink, destruction changes (madvise with
+   MADV_DONTNEED), or change its size.  The input has the same constraints
+   as the mseal syscall.
+
+   Return 0 in case of success or a negative value otherwise (a negative
+   errno).  */
+int _dl_mseal (void *addr, size_t len) attribute_hidden;
+
+#define SUPPORT_MSEAL 1

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [glibc/azanella/mseal] elf: Add support to memory sealing
@ 2024-06-21 14:30 Adhemerval Zanella
  0 siblings, 0 replies; 5+ messages in thread
From: Adhemerval Zanella @ 2024-06-21 14:30 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=6ea619cd600b708309fa0c7d8b6b4ee3756223e0

commit 6ea619cd600b708309fa0c7d8b6b4ee3756223e0
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Sun Jun 2 20:31:27 2024 -0300

    elf: Add support to memory sealing
    
    The new Linux mseal syscall allows seal memory mappings to avoid
    further changes such as memory protection or remap.  The sealing
    is done in multiple places where the memory 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
        It is up either to binary or the loader to set up the sealing.
    
      * The vDSO vma provided by the kernel (if existent).
    
      * Any preload libraries.
    
      * Any library loaded with dlopen with RTLD_NODELETE flag.
    
    For binary dependencies, the RTLD_NODELETE signals the
    link_map should be sealed.  It also makes dlopen objects with the
    flag sealed as well.
    
    The sealing is controlled by a new tunable, glibc.rtld.seal, with
    three different states:
    
      0. Disabled where no sealing is done.  This is the default.
    
      1. Enabled, where the loader will issue the mseal syscall on the
         memory mappings but any failure will be ignored.  This is
         the default.
    
      2. Enforce, similar to Enabled but any failure from the mseal
         will terminate the process.
    
    Checked on x86_64-linux-gnu and aarch64-linux-gnu.

Diff:
---
 elf/dl-load.c                                      |   2 +
 elf/dl-mseal-mode.h                                |  29 +++
 elf/dl-open.c                                      |   4 +
 elf/dl-reloc.c                                     |  49 ++++
 elf/dl-support.c                                   |   7 +
 elf/dl-tunables.list                               |   6 +
 elf/rtld.c                                         |  10 +-
 elf/setup-vdso.h                                   |   3 +
 elf/tst-rtld-list-tunables.exp                     |   1 +
 include/link.h                                     |   6 +
 manual/tunables.texi                               |  35 +++
 string/strerrorname_np.c                           |   1 +
 sysdeps/generic/dl-mseal.h                         |  25 ++
 sysdeps/generic/ldsodefs.h                         |   6 +
 sysdeps/unix/sysv/linux/Makefile                   |  45 ++++
 sysdeps/unix/sysv/linux/dl-mseal.c                 |  51 ++++
 sysdeps/unix/sysv/linux/dl-mseal.h                 |  29 +++
 sysdeps/unix/sysv/linux/lib-tst-dl_mseal-1.c       |  19 ++
 sysdeps/unix/sysv/linux/lib-tst-dl_mseal-2.c       |  19 ++
 .../unix/sysv/linux/lib-tst-dl_mseal-dlopen-1-1.c  |  19 ++
 .../unix/sysv/linux/lib-tst-dl_mseal-dlopen-1.c    |  19 ++
 .../unix/sysv/linux/lib-tst-dl_mseal-dlopen-2-1.c  |  19 ++
 .../unix/sysv/linux/lib-tst-dl_mseal-dlopen-2.c    |  19 ++
 sysdeps/unix/sysv/linux/tst-dl_mseal-static.c      |   2 +
 sysdeps/unix/sysv/linux/tst-dl_mseal.c             | 267 +++++++++++++++++++++
 25 files changed, 689 insertions(+), 3 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 8a89b71016..4c2371ec46 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1431,6 +1431,8 @@ cannot enable executable stack as shared object requires");
     /* Assign the next available module ID.  */
     _dl_assign_tls_modid (l);
 
+  l->l_seal = mode & RTLD_NODELETE ? lt_seal_toseal : lt_seal_dont;
+
 #ifdef DL_AFTER_LOAD
   DL_AFTER_LOAD (l);
 #endif
diff --git a/elf/dl-mseal-mode.h b/elf/dl-mseal-mode.h
new file mode 100644
index 0000000000..7f9ede4db7
--- /dev/null
+++ b/elf/dl-mseal-mode.h
@@ -0,0 +1,29 @@
+/* Memory sealing.  Generic definitions.
+   Copyright (C) 2024 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/>.  */
+
+#ifndef _DL_SEAL_MODE_H
+#define _DL_SEAL_MODE_H
+
+enum dl_seal_mode
+{
+  DL_SEAL_DISABLE = 0,
+  DL_SEAL_ENABLE = 1,
+  DL_SEAL_ENFORCE = 2,
+};
+
+#endif
diff --git a/elf/dl-open.c b/elf/dl-open.c
index c378da16c0..7bd90ef069 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -837,6 +837,10 @@ dl_open_worker (void *a)
   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
     _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
 		      new->l_name, new->l_ns, new->l_direct_opencount);
+
+  /* The seal flag is set only for NEW, however its dependencies could not be
+     unloaded and thus can also be sealed.  */
+  _dl_mseal_map (new, true);
 }
 
 void *
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 4bf7aec88b..01f88e9003 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -28,6 +28,7 @@
 #include <_itoa.h>
 #include <libc-pointer-arith.h>
 #include "dynamic-link.h"
+#include <dl-mseal.h>
 
 /* Statistics function.  */
 #ifdef SHARED
@@ -347,6 +348,11 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
      done, do it.  */
   if (l->l_relro_size != 0)
     _dl_protect_relro (l);
+
+  /* Seal the memory mapping after RELRO setup, we can use the PT_LOAD
+     segments because even if relro splits the the original RW VMA,
+     mseal works with multiple VMAs with different flags.  */
+  _dl_mseal_map (l, false);
 }
 
 
@@ -369,6 +375,49 @@ cannot apply additional memory protection after relocation");
     }
 }
 
+static void
+_dl_mseal_map_1 (struct link_map *l)
+{
+  if (l->l_seal == lt_seal_sealed)
+    return;
+
+  int r = -1;
+  if (l->l_contiguous)
+    r = _dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
+  else
+    {
+      const ElfW(Phdr) *ph;
+      for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
+	switch (ph->p_type)
+	  {
+	  case PT_LOAD:
+	    {
+	      ElfW(Addr) mapstart = l->l_addr
+		  + (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1));
+	      ElfW(Addr) allocend = l->l_addr + ph->p_vaddr + ph->p_memsz;
+	      r = _dl_mseal ((void *) mapstart, allocend - mapstart);
+	    }
+	    break;
+	}
+    }
+
+  if (r == 0)
+    l->l_seal = lt_seal_sealed;
+}
+
+void
+_dl_mseal_map (struct link_map *l, bool dep)
+{
+  if (l->l_seal == lt_seal_dont || l->l_nodelete_pending)
+    return;
+
+  if (l->l_searchlist.r_list == NULL || !dep)
+    _dl_mseal_map_1 (l);
+  else
+    for (unsigned int i = 0; i < l->l_searchlist.r_nlist; ++i)
+      _dl_mseal_map_1 (l->l_searchlist.r_list[i]);
+}
+
 void
 __attribute_noinline__
 _dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt)
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 451932dd03..8290a380f3 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -45,6 +45,7 @@
 #include <dl-find_object.h>
 #include <array_length.h>
 #include <dl-symbol-redir-ifunc.h>
+#include <dl-mseal.h>
 
 extern char *__progname;
 char **_dl_argv = &__progname;	/* This is checked for some error messages.  */
@@ -99,6 +100,7 @@ static struct link_map _dl_main_map =
     .l_used = 1,
     .l_tls_offset = NO_TLS_OFFSET,
     .l_serial = 1,
+    .l_seal = SUPPORT_MSEAL,
   };
 
 /* Namespace information.  */
@@ -340,6 +342,11 @@ _dl_non_dynamic_init (void)
   /* Setup relro on the binary itself.  */
   if (_dl_main_map.l_relro_size != 0)
     _dl_protect_relro (&_dl_main_map);
+
+  /* Seal the memory mapping after RELRO setup, we can use the PT_LOAD
+     segments because even if relro splits the the original RW VMA,
+     mseal works with multiple VMAs with different flags.  */
+  _dl_mseal_map (&_dl_main_map, false);
 }
 
 #ifdef DL_SYSINFO_IMPLEMENTATION
diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index 40ac5b3776..5eb3a2380b 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -135,6 +135,12 @@ glibc {
       maxval: 1
       default: 0
     }
+    seal {
+      type: INT_32
+      minval: 0
+      maxval: 2
+      default: 1
+    }
   }
 
   mem {
diff --git a/elf/rtld.c b/elf/rtld.c
index e9525ea987..174389e205 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -53,6 +53,7 @@
 #include <dl-find_object.h>
 #include <dl-audit-check.h>
 #include <dl-call_tls_init_tp.h>
+#include <dl-mseal.h>
 
 #include <assert.h>
 
@@ -477,6 +478,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
   GL(dl_rtld_map).l_real = &GL(dl_rtld_map);
   GL(dl_rtld_map).l_map_start = (ElfW(Addr)) &__ehdr_start;
   GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;
+  GL(dl_rtld_map).l_seal = 1;
   /* Copy the TLS related data if necessary.  */
 #ifndef DONT_USE_BOOTSTRAP_MAP
 # if NO_TLS_OFFSET != 0
@@ -809,7 +811,8 @@ do_preload (const char *fname, struct link_map *main_map, const char *where)
 
   args.str = fname;
   args.loader = main_map;
-  args.mode = __RTLD_SECURE;
+  /* RTLD_NODELETE enables sealing.  */
+  args.mode = __RTLD_SECURE | RTLD_NODELETE;
 
   unsigned int old_nloaded = GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
 
@@ -1123,6 +1126,7 @@ rtld_setup_main_map (struct link_map *main_map)
   /* And it was opened directly.  */
   ++main_map->l_direct_opencount;
   main_map->l_contiguous = 1;
+  main_map->l_seal = 1;
 
   /* A PT_LOAD segment at an unexpected address will clear the
      l_contiguous flag.  The ELF specification says that PT_LOAD
@@ -1636,7 +1640,7 @@ dl_main (const ElfW(Phdr) *phdr,
       /* Create a link_map for the executable itself.
 	 This will be what dlopen on "" returns.  */
       main_map = _dl_new_object ((char *) "", "", lt_executable, NULL,
-				 __RTLD_OPENEXEC, LM_ID_BASE);
+				 __RTLD_OPENEXEC | RTLD_NODELETE, LM_ID_BASE);
       assert (main_map != NULL);
       main_map->l_phdr = phdr;
       main_map->l_phnum = phnum;
@@ -1964,7 +1968,7 @@ dl_main (const ElfW(Phdr) *phdr,
     RTLD_TIMING_VAR (start);
     rtld_timer_start (&start);
     _dl_map_object_deps (main_map, preloads, npreloads,
-			 state.mode == rtld_mode_trace, 0);
+			 state.mode == rtld_mode_trace, RTLD_NODELETE);
     rtld_timer_accum (&load_time, start);
   }
 
diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h
index 888e1e4897..f8d9c36453 100644
--- a/elf/setup-vdso.h
+++ b/elf/setup-vdso.h
@@ -66,6 +66,7 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
 
       /* The vDSO is always used.  */
       l->l_used = 1;
+      l->l_seal = lt_seal_toseal;
 
       /* Initialize l_local_scope to contain just this map.  This allows
 	 the use of dl_lookup_symbol_x to resolve symbols within the vdso.
@@ -104,6 +105,8 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
       if (GLRO(dl_sysinfo) == DL_SYSINFO_DEFAULT)
 	GLRO(dl_sysinfo) = GLRO(dl_sysinfo_dso)->e_entry + l->l_addr;
 # endif
+
+      _dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
     }
 #endif
 }
diff --git a/elf/tst-rtld-list-tunables.exp b/elf/tst-rtld-list-tunables.exp
index db0e1c86e9..d40a478dd7 100644
--- a/elf/tst-rtld-list-tunables.exp
+++ b/elf/tst-rtld-list-tunables.exp
@@ -15,3 +15,4 @@ glibc.rtld.dynamic_sort: 2 (min: 1, max: 2)
 glibc.rtld.enable_secure: 0 (min: 0, max: 1)
 glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
 glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0x[f]+)
+glibc.rtld.seal: 1 (min: 0, max: 2)
diff --git a/include/link.h b/include/link.h
index cb0d7d8e2f..fd8e7f25bf 100644
--- a/include/link.h
+++ b/include/link.h
@@ -212,6 +212,12 @@ struct link_map
     unsigned int l_find_object_processed:1; /* Zero if _dl_find_object_update
 					       needs to process this
 					       lt_library map.  */
+    enum			/* Memory sealing status.  */
+      {
+	lt_seal_dont,		/* Do not seal the object.  */
+	lt_seal_toseal,		/* The library is marked to be sealed.  */
+	lt_seal_sealed		/* The library is sealed.  */
+      } l_seal:2;
 
     /* NODELETE status of the map.  Only valid for maps of type
        lt_loaded.  Lazy binding sets l_nodelete_active directly,
diff --git a/manual/tunables.texi b/manual/tunables.texi
index 0b1b2898c0..65ec47c3d2 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -355,6 +355,41 @@ tests for @code{AT_SECURE} programs and not meant to be a security feature.
 The default value of this tunable is @samp{0}.
 @end deftp
 
+@deftp Tunable glibc.rtld.seal
+Sets whether to enable memory sealing during program execution.  The sealed
+memory prevents further changes to the maped memory region, such as shrinking
+or expanding, mapping another segment over a pre-existing region, or change
+the memory protection flags (check the @code{mseal} for more information).
+The sealing is done in multiple places where the memory is supposed to be
+immuatable over program execution:
+
+@itemize @bullet
+@item
+All shared library dependencies from the binary, including the read-only segments
+after @code{PT_GNU_RELRO} setup.
+
+@item
+The binary itself, including dynamic and static linked.  In both cases it is up
+either to binary or the loader to setup the sealing.
+
+@item
+The vDSO vma provided by the kernel (if existent).
+
+@item
+Any preload libraries.
+
+@item
+Any library loaded with @code{dlopen} with @code{RTLD_NODELETE} flag.
+@end itemize
+
+The tunable accepts three diferent values: @samp{0} where sealing is disabled,
+@samp{1} where sealing is enabled, and @samp{2} where sealing is enforced.  For
+the enforced mode, if the memory can not be sealed the process terminates the
+execution.
+
+The default value of this tunable is @samp{1}.
+@end deftp
+
 @node Elision Tunables
 @section Elision Tunables
 @cindex elision tunables
diff --git a/string/strerrorname_np.c b/string/strerrorname_np.c
index 042cea381c..e0e22fa79e 100644
--- a/string/strerrorname_np.c
+++ b/string/strerrorname_np.c
@@ -17,6 +17,7 @@
    <https://www.gnu.org/licenses/>.  */
 
 #include <stdio.h>
+#include <string.h>
 
 const char *
 strerrorname_np (int errnum)
diff --git a/sysdeps/generic/dl-mseal.h b/sysdeps/generic/dl-mseal.h
new file mode 100644
index 0000000000..d542fcac75
--- /dev/null
+++ b/sysdeps/generic/dl-mseal.h
@@ -0,0 +1,25 @@
+/* Memory sealing.  Generic version.
+   Copyright (C) 2024 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/>.  */
+
+static inline int
+_dl_mseal (void *addr, size_t len)
+{
+  return 0;
+}
+
+#define SUPPORT_MSEAL lt_seal_dont
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 50f58a60e3..e0d46e9177 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1017,6 +1017,12 @@ extern void _dl_relocate_object (struct link_map *map,
 /* Protect PT_GNU_RELRO area.  */
 extern void _dl_protect_relro (struct link_map *map) attribute_hidden;
 
+/* Protect MAP with mseal.  If MAP is contiguous the while region is
+   sealed, otherwise iterate over the phdr to seal each PT_LOAD.  The DEP
+   specify whether to seal the dependencies as well.  */
+extern void _dl_mseal_map (struct link_map *map, bool dep)
+     attribute_hidden;
+
 /* Call _dl_signal_error with a message about an unhandled reloc type.
    TYPE is the result of ELFW(R_TYPE) (r_info), i.e. an R_<CPU>_* value.
    PLT is nonzero if this was a PLT reloc; it just affects the message.  */
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 82d523e588..808f9e5f8c 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -625,6 +625,10 @@ sysdep-rtld-routines += \
   dl-sbrk \
   # sysdep-rtld-routines
 
+dl-routines += \
+  dl-mseal \
+  # dl-routines
+
 others += \
   pldd \
   # others
@@ -634,6 +638,47 @@ install-bin += \
   # install-bin
 
 $(objpfx)pldd: $(objpfx)xmalloc.o
+
+tests-static += \
+  tst-dl_mseal-static \
+  # tests-static
+
+tests += \
+  $(tests-static) \
+  tst-dl_mseal \
+  # tests
+
+modules-names += \
+  lib-tst-dl_mseal-1 \
+  lib-tst-dl_mseal-2 \
+  lib-tst-dl_mseal-dlopen-1 \
+  lib-tst-dl_mseal-dlopen-1-1 \
+  lib-tst-dl_mseal-dlopen-2 \
+  lib-tst-dl_mseal-dlopen-2-1 \
+  lib-tst-dl_mseal-preload \
+  # modules-names
+
+$(objpfx)tst-dl_mseal.out: \
+  $(objpfx)lib-tst-dl_mseal-preload.so \
+  $(objpfx)lib-tst-dl_mseal-1.so \
+  $(objpfx)lib-tst-dl_mseal-2.so \
+  $(objpfx)lib-tst-dl_mseal-dlopen-1.so \
+  $(objpfx)lib-tst-dl_mseal-dlopen-1-1.so \
+  $(objpfx)lib-tst-dl_mseal-dlopen-2.so \
+  $(objpfx)lib-tst-dl_mseal-dlopen-2-1.so
+
+tst-dl_mseal-ARGS = -- $(host-test-program-cmd)
+$(objpfx)tst-dl_mseal: $(objpfx)lib-tst-dl_mseal-1.so
+LDFLAGS-tst-dl_mseal = -Wl,--no-as-needed
+$(objpfx)lib-tst-dl_mseal-1.so: $(objpfx)lib-tst-dl_mseal-2.so
+LDFLAGS-lib-tst-dl_mseal-1.so = -Wl,--no-as-needed
+
+$(objpfx)lib-tst-dl_mseal-dlopen-1.so: $(objpfx)lib-tst-dl_mseal-dlopen-1-1.so
+LDFLAGS-lib-tst-dl_mseal-dlopen-1.so = -Wl,--no-as-needed
+$(objpfx)lib-tst-dl_mseal-dlopen-2.so: $(objpfx)lib-tst-dl_mseal-dlopen-2-1.so
+LDFLAGS-lib-tst-dl_mseal-dlopen-2.so = -Wl,--no-as-needed
+
+tst-dl_mseal-static-ARGS = -- $(host-test-program-cmd)
 endif
 
 ifeq ($(subdir),rt)
diff --git a/sysdeps/unix/sysv/linux/dl-mseal.c b/sysdeps/unix/sysv/linux/dl-mseal.c
new file mode 100644
index 0000000000..69124b34af
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.c
@@ -0,0 +1,51 @@
+/* Memory sealing.  Linux version.
+   Copyright (C) 2024 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 <atomic.h>
+#include <dl-mseal.h>
+#include <dl-mseal-mode.h>
+#include <dl-tunables.h>
+#include <ldsodefs.h>
+
+int
+_dl_mseal (void *addr, size_t len)
+{
+  int32_t mode = TUNABLE_GET (glibc, rtld, seal, int32_t, NULL);
+  if (mode == DL_SEAL_DISABLE)
+    return 0;
+
+  int r;
+#if __ASSUME_MSEAL
+  r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0);
+#else
+  r = -ENOSYS;
+  static int mseal_supported = true;
+  if (atomic_load_relaxed (&mseal_supported))
+    {
+      r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0);
+      if (r == -ENOSYS)
+	atomic_store_relaxed (&mseal_supported, false);
+    }
+#endif
+  if (mode == DL_SEAL_ENFORCE && r != 0)
+    _dl_fatal_printf ("Fatal error: sealing is enforced and an error "
+		      "ocurred for the 0x%lx-0x%lx range\n",
+		      (long unsigned int) addr,
+		      (long unsigned int) addr + len);
+  return r;
+}
diff --git a/sysdeps/unix/sysv/linux/dl-mseal.h b/sysdeps/unix/sysv/linux/dl-mseal.h
new file mode 100644
index 0000000000..89b19e33c4
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.h
@@ -0,0 +1,29 @@
+/* Memory sealing.  Linux version.
+   Copyright (C) 2024 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/>.  */
+
+/* Seal the ADDR or size LEN to protect against modifications, such as
+   changes on the permission flags (through mprotect), remap (through
+   mmap and/or remap), shrink, destruction changes (madvise with
+   MADV_DONTNEED), or change its size.  The input has the same constraints
+   as the mseal syscall.
+
+   Return 0 in case of success or a negative value otherwise (a negative
+   errno).  */
+int _dl_mseal (void *addr, size_t len) attribute_hidden;
+
+#define SUPPORT_MSEAL lt_seal_toseal
diff --git a/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-1.c b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-1.c
new file mode 100644
index 0000000000..3bd188efe8
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-1.c
@@ -0,0 +1,19 @@
+/* Additional module for tst-dl_mseal test.
+   Copyright (C) 2024 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 foo1 (void) { return 42; }
diff --git a/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-2.c b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-2.c
new file mode 100644
index 0000000000..636e9777af
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-2.c
@@ -0,0 +1,19 @@
+/* Additional module for tst-dl_mseal test.
+   Copyright (C) 2024 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 bar1 (void) { return 42; }
diff --git a/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-1-1.c b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-1-1.c
new file mode 100644
index 0000000000..ef1372f47e
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-1-1.c
@@ -0,0 +1,19 @@
+/* Additional module for tst-dl_mseal test.
+   Copyright (C) 2024 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 foo2_1 (void) { return 42; }
diff --git a/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-1.c b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-1.c
new file mode 100644
index 0000000000..3c2cbe6035
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-1.c
@@ -0,0 +1,19 @@
+/* Additional module for tst-dl_mseal test.
+   Copyright (C) 2024 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 foo2 (void) { return 42; }
diff --git a/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-2-1.c b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-2-1.c
new file mode 100644
index 0000000000..0cd647de46
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-2-1.c
@@ -0,0 +1,19 @@
+/* Additional module for tst-dl_mseal test.
+   Copyright (C) 2024 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 bar2_1 (void) { return 42; }
diff --git a/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-2.c b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-2.c
new file mode 100644
index 0000000000..f719dd3cba
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-2.c
@@ -0,0 +1,19 @@
+/* Additional module for tst-dl_mseal test.
+   Copyright (C) 2024 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 bar2 (void) { return 42; }
diff --git a/sysdeps/unix/sysv/linux/tst-dl_mseal-static.c b/sysdeps/unix/sysv/linux/tst-dl_mseal-static.c
new file mode 100644
index 0000000000..7f26713b35
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-dl_mseal-static.c
@@ -0,0 +1,2 @@
+#define TEST_STATIC
+#include "tst-dl_mseal.c"
diff --git a/sysdeps/unix/sysv/linux/tst-dl_mseal.c b/sysdeps/unix/sysv/linux/tst-dl_mseal.c
new file mode 100644
index 0000000000..72a33d04c7
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-dl_mseal.c
@@ -0,0 +1,267 @@
+/* Basic tests for sealing.
+   Copyright (C) 2024 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 <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xdlfcn.h>
+#include <support/xstdio.h>
+
+#define LIB_PRELOAD              "lib-tst-dl_mseal-preload.so"
+
+#define LIB_NEEDED_1             "lib-tst-dl_mseal-1.so"
+#define LIB_NEEDED_2             "lib-tst-dl_mseal-2.so"
+
+#define LIB_DLOPEN_DEFAULT       "lib-tst-dl_mseal-dlopen-1.so"
+#define LIB_DLOPEN_DEFAULT_DEP   "lib-tst-dl_mseal-dlopen-1-1.so"
+#define LIB_DLOPEN_NODELETE      "lib-tst-dl_mseal-dlopen-2.so"
+#define LIB_DLOPEN_NODELETE_DEP  "lib-tst-dl_mseal-dlopen-2-1.so"
+
+static int
+new_flags (const char flags[4])
+{
+  bool read_flag  = flags[0] == 'r';
+  bool write_flag = flags[1] == 'w';
+  bool exec_flag  = flags[2] == 'x';
+
+  write_flag = !write_flag;
+
+  return (read_flag ? PROT_READ : 0)
+	 | (write_flag ? PROT_WRITE : 0)
+	 | (exec_flag ? PROT_EXEC : 0);
+}
+
+/* Expected libraries that loader will seal.  */
+static const char *expected_sealed_libs[] =
+{
+#ifdef TEST_STATIC
+  "tst-dl_mseal-static",
+#else
+  "libc.so",
+  "ld.so",
+  "tst-dl_mseal",
+  LIB_PRELOAD,
+  LIB_NEEDED_1,
+  LIB_NEEDED_2,
+  LIB_DLOPEN_NODELETE,
+  LIB_DLOPEN_NODELETE_DEP,
+#endif
+  "[vdso]",
+};
+
+/* Libraries/VMA that could not be sealed.  */
+static const char *non_sealed_vmas[] =
+{
+  ".",				/* basename value for empty string anonymous
+				   mappings.  */
+  "[heap]",
+  "[vsyscall]",
+  "[vvar]",
+  "[stack]",
+  "zero",			/* /dev/zero  */
+#ifndef TEST_STATIC
+  "tst-dl_mseal-mod-2.so",
+  LIB_DLOPEN_DEFAULT,
+  LIB_DLOPEN_DEFAULT_DEP
+#endif
+};
+
+static int
+is_in_string_list (const char *s, const char *const list[], size_t len)
+{
+  for (size_t i = 0; i != len; i++)
+    if (strcmp (s, list[i]) == 0)
+      return i;
+  return -1;
+}
+
+static int
+handle_restart (void)
+{
+#ifndef TEST_STATIC
+  xdlopen (LIB_DLOPEN_NODELETE, RTLD_NOW | RTLD_NODELETE);
+  xdlopen (LIB_DLOPEN_DEFAULT, RTLD_NOW);
+#endif
+
+  FILE *fp = xfopen ("/proc/self/maps", "r");
+  char *line = NULL;
+  size_t linesiz = 0;
+
+  unsigned long pagesize = getpagesize ();
+
+  bool found_expected[array_length(expected_sealed_libs)] = { false };
+  while (xgetline (&line, &linesiz, fp) > 0)
+    {
+      uintptr_t start;
+      uintptr_t end;
+      char flags[5] = { 0 };
+      char name[256] = { 0 };
+      int idx;
+
+      /* The line is in the form:
+	 start-end flags offset dev inode pathname   */
+      int r = sscanf (line,
+		      "%" SCNxPTR "-%" SCNxPTR " %4s %*s %*s %*s %256s",
+		      &start,
+		      &end,
+		      flags,
+		      name);
+      TEST_VERIFY_EXIT (r == 3 || r == 4);
+
+      int found = false;
+
+      const char *libname = basename (name);
+      if ((idx = is_in_string_list (libname, expected_sealed_libs,
+				    array_length (expected_sealed_libs)))
+	   != -1)
+	{
+	  /* Check if we can change the protection flags of the segment.  */
+	  int new_prot = new_flags (flags);
+	  TEST_VERIFY_EXIT (mprotect ((void *) start, end - start,
+				      new_prot) == -1);
+	  TEST_VERIFY_EXIT (errno == EPERM);
+
+	  /* Also checks trying to map over the sealed libraries.  */
+	  {
+	    char *p = mmap ((void *) start, pagesize, new_prot,
+			    MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+	    TEST_VERIFY_EXIT (p == MAP_FAILED);
+	    TEST_VERIFY_EXIT (errno == EPERM);
+	  }
+
+	  /* And if remap is also blocked.  */
+	  {
+	    char *p = mremap ((void *) start, end - start, end - start, 0);
+	    TEST_VERIFY_EXIT (p == MAP_FAILED);
+	    TEST_VERIFY_EXIT (errno == EPERM);
+	  }
+
+	  printf ("sealed:     vma: %#" PRIxPTR "-%#" PRIxPTR " %s %s\n",
+		  start,
+		  end,
+		  flags,
+		  name);
+
+	  found_expected[idx] = true;
+	  found = true;
+	}
+
+      if (!found)
+	{
+	  if (is_in_string_list (libname, non_sealed_vmas,
+				 array_length (non_sealed_vmas)) != -1)
+	    printf ("not-sealed: vma: %#" PRIxPTR "-%#" PRIxPTR " %s %s\n",
+		    start,
+		    end,
+		    flags,
+		    name);
+	  else
+	    FAIL_EXIT1 ("unexpected vma: %#" PRIxPTR "-%#" PRIxPTR " %s %s\n",
+			start,
+			end,
+			flags,
+			name);
+	}
+    }
+  xfclose (fp);
+
+  printf ("\n");
+
+  /* Also check if all the expected sealed maps were found.  */
+  for (int i = 0; i < array_length (expected_sealed_libs); i++)
+    if (!found_expected[i])
+      FAIL_EXIT1 ("expected VMA %s not sealed\n", expected_sealed_libs[i]);
+
+  return 0;
+}
+
+static int restart;
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+
+static int
+do_test (int argc, char *argv[])
+{
+  /* We must have either:
+     - One or four parameters left if called initially:
+       + path to ld.so         optional
+       + "--library-path"      optional
+       + the library path      optional
+       + the application name  */
+  if (restart)
+    return handle_restart ();
+
+  /* Check the test requirements.  */
+  {
+    int r = mseal (NULL, 0, 0);
+    if (r == -1 && errno == ENOSYS)
+      FAIL_UNSUPPORTED ("mseal is not supported by the kernel");
+    else
+      TEST_VERIFY_EXIT (r == 0);
+  }
+  support_need_proc ("Reads /proc/self/maps to get stack names.");
+
+  char *spargv[9];
+  int i = 0;
+  for (; i < argc - 1; i++)
+    spargv[i] = argv[i + 1];
+  spargv[i++] = (char *) "--direct";
+  spargv[i++] = (char *) "--restart";
+  spargv[i] = NULL;
+
+  char *envvarss[3];
+  envvarss[0] = (char *) "GLIBC_TUNABLES=glibc.rtld.seal=2";
+#ifndef TEST_STATIC
+  envvarss[1] = (char *) "LD_PRELOAD=" LIB_PRELOAD;
+  envvarss[2] = NULL;
+#else
+  envvarss[1] = NULL;
+#endif
+
+  struct support_capture_subprocess result =
+    support_capture_subprogram (spargv[0], spargv, envvarss);
+  support_capture_subprocess_check (&result, "tst-dl_mseal", 0,
+				    sc_allow_stdout);
+
+  {
+    FILE *out = fmemopen (result.out.buffer, result.out.length, "r");
+    TEST_VERIFY (out != NULL);
+    char *line = NULL;
+    size_t linesz = 0;
+    while (xgetline (&line, &linesz, out))
+      printf ("%s", line);
+    fclose (out);
+  }
+
+  support_capture_subprocess_free (&result);
+
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [glibc/azanella/mseal] elf: Add support to memory sealing
@ 2024-06-19 13:04 Adhemerval Zanella
  0 siblings, 0 replies; 5+ messages in thread
From: Adhemerval Zanella @ 2024-06-19 13:04 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=3c83d0f2aeb8058149845f03b370f6522374999c

commit 3c83d0f2aeb8058149845f03b370f6522374999c
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Sun Jun 2 20:31:27 2024 -0300

    elf: Add support to memory sealing
    
    The new Linux mseal syscall allows seal memory mappings to avoid
    further changes such as memory protection or remap.  The sealing
    is done in multiple places where the memory 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
        It is up either to binary or the loader to set up the sealing.
    
      * The vDSO vma provided by the kernel (if existent).
    
      * Any preload libraries.
    
      * Any library loaded with dlopen with RTLD_NODELETE flag.
    
    For binary dependencies, the RTLD_NODELETE signals the
    link_map should be sealed.  It also makes dlopen objects with the
    flag sealed as well.
    
    The sealing is controlled by a new tunable, glibc.rtld.seal, with
    three different states:
    
      0. Disabled where no sealing is done.  This is the default.
    
      1. Enabled, where the loader will issue the mseal syscall on the
         memory mappings but any failure will be ignored.  This is
         the default.
    
      2. Enforce, similar to Enabled but any failure from the mseal
         will terminate the process.
    
    Checked on x86_64-linux-gnu and aarch64-linux-gnu.

Diff:
---
 elf/dl-load.c                                      |   2 +
 elf/dl-mseal-mode.h                                |  29 +++
 elf/dl-open.c                                      |   4 +
 elf/dl-reloc.c                                     |  49 ++++
 elf/dl-support.c                                   |   7 +
 elf/dl-tunables.list                               |   6 +
 elf/rtld.c                                         |  10 +-
 elf/setup-vdso.h                                   |   3 +
 elf/tst-rtld-list-tunables.exp                     |   1 +
 include/link.h                                     |   6 +
 manual/tunables.texi                               |  35 +++
 string/strerrorname_np.c                           |   1 +
 sysdeps/generic/dl-mseal.h                         |  25 ++
 sysdeps/generic/ldsodefs.h                         |   6 +
 sysdeps/unix/sysv/linux/Makefile                   |  45 ++++
 sysdeps/unix/sysv/linux/dl-mseal.c                 |  51 ++++
 sysdeps/unix/sysv/linux/dl-mseal.h                 |  29 +++
 sysdeps/unix/sysv/linux/lib-tst-dl_mseal-1.c       |  19 ++
 sysdeps/unix/sysv/linux/lib-tst-dl_mseal-2.c       |  19 ++
 .../unix/sysv/linux/lib-tst-dl_mseal-dlopen-1-1.c  |  19 ++
 .../unix/sysv/linux/lib-tst-dl_mseal-dlopen-1.c    |  19 ++
 .../unix/sysv/linux/lib-tst-dl_mseal-dlopen-2-1.c  |  19 ++
 .../unix/sysv/linux/lib-tst-dl_mseal-dlopen-2.c    |  19 ++
 sysdeps/unix/sysv/linux/tst-dl_mseal-static.c      |   2 +
 sysdeps/unix/sysv/linux/tst-dl_mseal.c             | 267 +++++++++++++++++++++
 25 files changed, 689 insertions(+), 3 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 8a89b71016..4c2371ec46 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1431,6 +1431,8 @@ cannot enable executable stack as shared object requires");
     /* Assign the next available module ID.  */
     _dl_assign_tls_modid (l);
 
+  l->l_seal = mode & RTLD_NODELETE ? lt_seal_toseal : lt_seal_dont;
+
 #ifdef DL_AFTER_LOAD
   DL_AFTER_LOAD (l);
 #endif
diff --git a/elf/dl-mseal-mode.h b/elf/dl-mseal-mode.h
new file mode 100644
index 0000000000..7f9ede4db7
--- /dev/null
+++ b/elf/dl-mseal-mode.h
@@ -0,0 +1,29 @@
+/* Memory sealing.  Generic definitions.
+   Copyright (C) 2024 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/>.  */
+
+#ifndef _DL_SEAL_MODE_H
+#define _DL_SEAL_MODE_H
+
+enum dl_seal_mode
+{
+  DL_SEAL_DISABLE = 0,
+  DL_SEAL_ENABLE = 1,
+  DL_SEAL_ENFORCE = 2,
+};
+
+#endif
diff --git a/elf/dl-open.c b/elf/dl-open.c
index c378da16c0..7bd90ef069 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -837,6 +837,10 @@ dl_open_worker (void *a)
   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
     _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
 		      new->l_name, new->l_ns, new->l_direct_opencount);
+
+  /* The seal flag is set only for NEW, however its dependencies could not be
+     unloaded and thus can also be sealed.  */
+  _dl_mseal_map (new, true);
 }
 
 void *
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 4bf7aec88b..01f88e9003 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -28,6 +28,7 @@
 #include <_itoa.h>
 #include <libc-pointer-arith.h>
 #include "dynamic-link.h"
+#include <dl-mseal.h>
 
 /* Statistics function.  */
 #ifdef SHARED
@@ -347,6 +348,11 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
      done, do it.  */
   if (l->l_relro_size != 0)
     _dl_protect_relro (l);
+
+  /* Seal the memory mapping after RELRO setup, we can use the PT_LOAD
+     segments because even if relro splits the the original RW VMA,
+     mseal works with multiple VMAs with different flags.  */
+  _dl_mseal_map (l, false);
 }
 
 
@@ -369,6 +375,49 @@ cannot apply additional memory protection after relocation");
     }
 }
 
+static void
+_dl_mseal_map_1 (struct link_map *l)
+{
+  if (l->l_seal == lt_seal_sealed)
+    return;
+
+  int r = -1;
+  if (l->l_contiguous)
+    r = _dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
+  else
+    {
+      const ElfW(Phdr) *ph;
+      for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
+	switch (ph->p_type)
+	  {
+	  case PT_LOAD:
+	    {
+	      ElfW(Addr) mapstart = l->l_addr
+		  + (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1));
+	      ElfW(Addr) allocend = l->l_addr + ph->p_vaddr + ph->p_memsz;
+	      r = _dl_mseal ((void *) mapstart, allocend - mapstart);
+	    }
+	    break;
+	}
+    }
+
+  if (r == 0)
+    l->l_seal = lt_seal_sealed;
+}
+
+void
+_dl_mseal_map (struct link_map *l, bool dep)
+{
+  if (l->l_seal == lt_seal_dont || l->l_nodelete_pending)
+    return;
+
+  if (l->l_searchlist.r_list == NULL || !dep)
+    _dl_mseal_map_1 (l);
+  else
+    for (unsigned int i = 0; i < l->l_searchlist.r_nlist; ++i)
+      _dl_mseal_map_1 (l->l_searchlist.r_list[i]);
+}
+
 void
 __attribute_noinline__
 _dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt)
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 451932dd03..8290a380f3 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -45,6 +45,7 @@
 #include <dl-find_object.h>
 #include <array_length.h>
 #include <dl-symbol-redir-ifunc.h>
+#include <dl-mseal.h>
 
 extern char *__progname;
 char **_dl_argv = &__progname;	/* This is checked for some error messages.  */
@@ -99,6 +100,7 @@ static struct link_map _dl_main_map =
     .l_used = 1,
     .l_tls_offset = NO_TLS_OFFSET,
     .l_serial = 1,
+    .l_seal = SUPPORT_MSEAL,
   };
 
 /* Namespace information.  */
@@ -340,6 +342,11 @@ _dl_non_dynamic_init (void)
   /* Setup relro on the binary itself.  */
   if (_dl_main_map.l_relro_size != 0)
     _dl_protect_relro (&_dl_main_map);
+
+  /* Seal the memory mapping after RELRO setup, we can use the PT_LOAD
+     segments because even if relro splits the the original RW VMA,
+     mseal works with multiple VMAs with different flags.  */
+  _dl_mseal_map (&_dl_main_map, false);
 }
 
 #ifdef DL_SYSINFO_IMPLEMENTATION
diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index 40ac5b3776..5eb3a2380b 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -135,6 +135,12 @@ glibc {
       maxval: 1
       default: 0
     }
+    seal {
+      type: INT_32
+      minval: 0
+      maxval: 2
+      default: 1
+    }
   }
 
   mem {
diff --git a/elf/rtld.c b/elf/rtld.c
index e9525ea987..174389e205 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -53,6 +53,7 @@
 #include <dl-find_object.h>
 #include <dl-audit-check.h>
 #include <dl-call_tls_init_tp.h>
+#include <dl-mseal.h>
 
 #include <assert.h>
 
@@ -477,6 +478,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
   GL(dl_rtld_map).l_real = &GL(dl_rtld_map);
   GL(dl_rtld_map).l_map_start = (ElfW(Addr)) &__ehdr_start;
   GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;
+  GL(dl_rtld_map).l_seal = 1;
   /* Copy the TLS related data if necessary.  */
 #ifndef DONT_USE_BOOTSTRAP_MAP
 # if NO_TLS_OFFSET != 0
@@ -809,7 +811,8 @@ do_preload (const char *fname, struct link_map *main_map, const char *where)
 
   args.str = fname;
   args.loader = main_map;
-  args.mode = __RTLD_SECURE;
+  /* RTLD_NODELETE enables sealing.  */
+  args.mode = __RTLD_SECURE | RTLD_NODELETE;
 
   unsigned int old_nloaded = GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
 
@@ -1123,6 +1126,7 @@ rtld_setup_main_map (struct link_map *main_map)
   /* And it was opened directly.  */
   ++main_map->l_direct_opencount;
   main_map->l_contiguous = 1;
+  main_map->l_seal = 1;
 
   /* A PT_LOAD segment at an unexpected address will clear the
      l_contiguous flag.  The ELF specification says that PT_LOAD
@@ -1636,7 +1640,7 @@ dl_main (const ElfW(Phdr) *phdr,
       /* Create a link_map for the executable itself.
 	 This will be what dlopen on "" returns.  */
       main_map = _dl_new_object ((char *) "", "", lt_executable, NULL,
-				 __RTLD_OPENEXEC, LM_ID_BASE);
+				 __RTLD_OPENEXEC | RTLD_NODELETE, LM_ID_BASE);
       assert (main_map != NULL);
       main_map->l_phdr = phdr;
       main_map->l_phnum = phnum;
@@ -1964,7 +1968,7 @@ dl_main (const ElfW(Phdr) *phdr,
     RTLD_TIMING_VAR (start);
     rtld_timer_start (&start);
     _dl_map_object_deps (main_map, preloads, npreloads,
-			 state.mode == rtld_mode_trace, 0);
+			 state.mode == rtld_mode_trace, RTLD_NODELETE);
     rtld_timer_accum (&load_time, start);
   }
 
diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h
index 888e1e4897..f8d9c36453 100644
--- a/elf/setup-vdso.h
+++ b/elf/setup-vdso.h
@@ -66,6 +66,7 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
 
       /* The vDSO is always used.  */
       l->l_used = 1;
+      l->l_seal = lt_seal_toseal;
 
       /* Initialize l_local_scope to contain just this map.  This allows
 	 the use of dl_lookup_symbol_x to resolve symbols within the vdso.
@@ -104,6 +105,8 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
       if (GLRO(dl_sysinfo) == DL_SYSINFO_DEFAULT)
 	GLRO(dl_sysinfo) = GLRO(dl_sysinfo_dso)->e_entry + l->l_addr;
 # endif
+
+      _dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
     }
 #endif
 }
diff --git a/elf/tst-rtld-list-tunables.exp b/elf/tst-rtld-list-tunables.exp
index db0e1c86e9..d40a478dd7 100644
--- a/elf/tst-rtld-list-tunables.exp
+++ b/elf/tst-rtld-list-tunables.exp
@@ -15,3 +15,4 @@ glibc.rtld.dynamic_sort: 2 (min: 1, max: 2)
 glibc.rtld.enable_secure: 0 (min: 0, max: 1)
 glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
 glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0x[f]+)
+glibc.rtld.seal: 1 (min: 0, max: 2)
diff --git a/include/link.h b/include/link.h
index cb0d7d8e2f..fd8e7f25bf 100644
--- a/include/link.h
+++ b/include/link.h
@@ -212,6 +212,12 @@ struct link_map
     unsigned int l_find_object_processed:1; /* Zero if _dl_find_object_update
 					       needs to process this
 					       lt_library map.  */
+    enum			/* Memory sealing status.  */
+      {
+	lt_seal_dont,		/* Do not seal the object.  */
+	lt_seal_toseal,		/* The library is marked to be sealed.  */
+	lt_seal_sealed		/* The library is sealed.  */
+      } l_seal:2;
 
     /* NODELETE status of the map.  Only valid for maps of type
        lt_loaded.  Lazy binding sets l_nodelete_active directly,
diff --git a/manual/tunables.texi b/manual/tunables.texi
index 0b1b2898c0..65ec47c3d2 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -355,6 +355,41 @@ tests for @code{AT_SECURE} programs and not meant to be a security feature.
 The default value of this tunable is @samp{0}.
 @end deftp
 
+@deftp Tunable glibc.rtld.seal
+Sets whether to enable memory sealing during program execution.  The sealed
+memory prevents further changes to the maped memory region, such as shrinking
+or expanding, mapping another segment over a pre-existing region, or change
+the memory protection flags (check the @code{mseal} for more information).
+The sealing is done in multiple places where the memory is supposed to be
+immuatable over program execution:
+
+@itemize @bullet
+@item
+All shared library dependencies from the binary, including the read-only segments
+after @code{PT_GNU_RELRO} setup.
+
+@item
+The binary itself, including dynamic and static linked.  In both cases it is up
+either to binary or the loader to setup the sealing.
+
+@item
+The vDSO vma provided by the kernel (if existent).
+
+@item
+Any preload libraries.
+
+@item
+Any library loaded with @code{dlopen} with @code{RTLD_NODELETE} flag.
+@end itemize
+
+The tunable accepts three diferent values: @samp{0} where sealing is disabled,
+@samp{1} where sealing is enabled, and @samp{2} where sealing is enforced.  For
+the enforced mode, if the memory can not be sealed the process terminates the
+execution.
+
+The default value of this tunable is @samp{1}.
+@end deftp
+
 @node Elision Tunables
 @section Elision Tunables
 @cindex elision tunables
diff --git a/string/strerrorname_np.c b/string/strerrorname_np.c
index 042cea381c..e0e22fa79e 100644
--- a/string/strerrorname_np.c
+++ b/string/strerrorname_np.c
@@ -17,6 +17,7 @@
    <https://www.gnu.org/licenses/>.  */
 
 #include <stdio.h>
+#include <string.h>
 
 const char *
 strerrorname_np (int errnum)
diff --git a/sysdeps/generic/dl-mseal.h b/sysdeps/generic/dl-mseal.h
new file mode 100644
index 0000000000..d542fcac75
--- /dev/null
+++ b/sysdeps/generic/dl-mseal.h
@@ -0,0 +1,25 @@
+/* Memory sealing.  Generic version.
+   Copyright (C) 2024 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/>.  */
+
+static inline int
+_dl_mseal (void *addr, size_t len)
+{
+  return 0;
+}
+
+#define SUPPORT_MSEAL lt_seal_dont
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 50f58a60e3..e0d46e9177 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1017,6 +1017,12 @@ extern void _dl_relocate_object (struct link_map *map,
 /* Protect PT_GNU_RELRO area.  */
 extern void _dl_protect_relro (struct link_map *map) attribute_hidden;
 
+/* Protect MAP with mseal.  If MAP is contiguous the while region is
+   sealed, otherwise iterate over the phdr to seal each PT_LOAD.  The DEP
+   specify whether to seal the dependencies as well.  */
+extern void _dl_mseal_map (struct link_map *map, bool dep)
+     attribute_hidden;
+
 /* Call _dl_signal_error with a message about an unhandled reloc type.
    TYPE is the result of ELFW(R_TYPE) (r_info), i.e. an R_<CPU>_* value.
    PLT is nonzero if this was a PLT reloc; it just affects the message.  */
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 82d523e588..922511b4a1 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -625,6 +625,10 @@ sysdep-rtld-routines += \
   dl-sbrk \
   # sysdep-rtld-routines
 
+dl-routines += \
+  dl-mseal \
+  # dl-routines
+
 others += \
   pldd \
   # others
@@ -634,6 +638,47 @@ install-bin += \
   # install-bin
 
 $(objpfx)pldd: $(objpfx)xmalloc.o
+
+tests-static += \
+  tst-dl_mseal-static \
+  # tests-static
+
+tests += \
+  $(tests-static) \
+  tst-dl_mseal \
+  # tests
+
+modules-names += \
+  lib-tst-dl_mseal-1 \
+  lib-tst-dl_mseal-2 \
+  lib-tst-dl_mseal-dlopen-1 \
+  lib-tst-dl_mseal-dlopen-1-1 \
+  lib-tst-dl_mseal-dlopen-2 \
+  lib-tst-dl_mseal-dlopen-2-1 \
+  lib-tst-dl_mseal-preload \
+  # modules-names
+
+$(objpfx)tst-dl_mseal.out: \
+  $(objpfx)lib-tst-dl_mseal-preload.so \
+  $(objpfx)lib-tst-dl_mseal-1.so \
+  $(objpfx)lib-tst-dl_mseal-2.so \
+  $(objpfx)lib-tst-dl_mseal-dlopen-1.so \
+  $(objpfx)lib-tst-dl_mseal-dlopen-1-1.so \
+  $(objpfx)lib-tst-dl_mseal-dlopen-2.so \
+  $(objpfx)lib-tst-dl_mseal-dlopen-2-1.so
+
+tst-dl_mseal-ARGS = -- $(host-test-program-cmd)
+$(objpfx)tst-dl_mseal: $(objpfx)lib-tst-dl_mseal-1.so
+$(objpfx)lib-tst-dl_mseal-1.so: $(objpfx)lib-tst-dl_mseal-2.so
+
+$(objpfx)lib-tst-dl_mseal-dlopen-1.so: $(objpfx)lib-tst-dl_mseal-dlopen-1-1.so
+$(objpfx)lib-tst-dl_mseal-dlopen-2.so: $(objpfx)lib-tst-dl_mseal-dlopen-2-1.so
+LDFLAGS-lib-tst-dl_mseal-dlopen-1.so = \
+  -Wl,-soname,lib-tst-dl_mseal-dlopen-1.so
+LDFLAGS-lib-tst-dl_mseal-dlopen-2.so = \
+  -Wl,-soname,lib-tst-dl_mseal-dlopen-2.so
+
+tst-dl_mseal-static-ARGS = -- $(host-test-program-cmd)
 endif
 
 ifeq ($(subdir),rt)
diff --git a/sysdeps/unix/sysv/linux/dl-mseal.c b/sysdeps/unix/sysv/linux/dl-mseal.c
new file mode 100644
index 0000000000..69124b34af
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.c
@@ -0,0 +1,51 @@
+/* Memory sealing.  Linux version.
+   Copyright (C) 2024 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 <atomic.h>
+#include <dl-mseal.h>
+#include <dl-mseal-mode.h>
+#include <dl-tunables.h>
+#include <ldsodefs.h>
+
+int
+_dl_mseal (void *addr, size_t len)
+{
+  int32_t mode = TUNABLE_GET (glibc, rtld, seal, int32_t, NULL);
+  if (mode == DL_SEAL_DISABLE)
+    return 0;
+
+  int r;
+#if __ASSUME_MSEAL
+  r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0);
+#else
+  r = -ENOSYS;
+  static int mseal_supported = true;
+  if (atomic_load_relaxed (&mseal_supported))
+    {
+      r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0);
+      if (r == -ENOSYS)
+	atomic_store_relaxed (&mseal_supported, false);
+    }
+#endif
+  if (mode == DL_SEAL_ENFORCE && r != 0)
+    _dl_fatal_printf ("Fatal error: sealing is enforced and an error "
+		      "ocurred for the 0x%lx-0x%lx range\n",
+		      (long unsigned int) addr,
+		      (long unsigned int) addr + len);
+  return r;
+}
diff --git a/sysdeps/unix/sysv/linux/dl-mseal.h b/sysdeps/unix/sysv/linux/dl-mseal.h
new file mode 100644
index 0000000000..89b19e33c4
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.h
@@ -0,0 +1,29 @@
+/* Memory sealing.  Linux version.
+   Copyright (C) 2024 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/>.  */
+
+/* Seal the ADDR or size LEN to protect against modifications, such as
+   changes on the permission flags (through mprotect), remap (through
+   mmap and/or remap), shrink, destruction changes (madvise with
+   MADV_DONTNEED), or change its size.  The input has the same constraints
+   as the mseal syscall.
+
+   Return 0 in case of success or a negative value otherwise (a negative
+   errno).  */
+int _dl_mseal (void *addr, size_t len) attribute_hidden;
+
+#define SUPPORT_MSEAL lt_seal_toseal
diff --git a/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-1.c b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-1.c
new file mode 100644
index 0000000000..3bd188efe8
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-1.c
@@ -0,0 +1,19 @@
+/* Additional module for tst-dl_mseal test.
+   Copyright (C) 2024 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 foo1 (void) { return 42; }
diff --git a/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-2.c b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-2.c
new file mode 100644
index 0000000000..636e9777af
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-2.c
@@ -0,0 +1,19 @@
+/* Additional module for tst-dl_mseal test.
+   Copyright (C) 2024 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 bar1 (void) { return 42; }
diff --git a/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-1-1.c b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-1-1.c
new file mode 100644
index 0000000000..ef1372f47e
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-1-1.c
@@ -0,0 +1,19 @@
+/* Additional module for tst-dl_mseal test.
+   Copyright (C) 2024 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 foo2_1 (void) { return 42; }
diff --git a/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-1.c b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-1.c
new file mode 100644
index 0000000000..3c2cbe6035
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-1.c
@@ -0,0 +1,19 @@
+/* Additional module for tst-dl_mseal test.
+   Copyright (C) 2024 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 foo2 (void) { return 42; }
diff --git a/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-2-1.c b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-2-1.c
new file mode 100644
index 0000000000..0cd647de46
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-2-1.c
@@ -0,0 +1,19 @@
+/* Additional module for tst-dl_mseal test.
+   Copyright (C) 2024 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 bar2_1 (void) { return 42; }
diff --git a/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-2.c b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-2.c
new file mode 100644
index 0000000000..f719dd3cba
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/lib-tst-dl_mseal-dlopen-2.c
@@ -0,0 +1,19 @@
+/* Additional module for tst-dl_mseal test.
+   Copyright (C) 2024 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 bar2 (void) { return 42; }
diff --git a/sysdeps/unix/sysv/linux/tst-dl_mseal-static.c b/sysdeps/unix/sysv/linux/tst-dl_mseal-static.c
new file mode 100644
index 0000000000..7f26713b35
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-dl_mseal-static.c
@@ -0,0 +1,2 @@
+#define TEST_STATIC
+#include "tst-dl_mseal.c"
diff --git a/sysdeps/unix/sysv/linux/tst-dl_mseal.c b/sysdeps/unix/sysv/linux/tst-dl_mseal.c
new file mode 100644
index 0000000000..72a33d04c7
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-dl_mseal.c
@@ -0,0 +1,267 @@
+/* Basic tests for sealing.
+   Copyright (C) 2024 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 <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xdlfcn.h>
+#include <support/xstdio.h>
+
+#define LIB_PRELOAD              "lib-tst-dl_mseal-preload.so"
+
+#define LIB_NEEDED_1             "lib-tst-dl_mseal-1.so"
+#define LIB_NEEDED_2             "lib-tst-dl_mseal-2.so"
+
+#define LIB_DLOPEN_DEFAULT       "lib-tst-dl_mseal-dlopen-1.so"
+#define LIB_DLOPEN_DEFAULT_DEP   "lib-tst-dl_mseal-dlopen-1-1.so"
+#define LIB_DLOPEN_NODELETE      "lib-tst-dl_mseal-dlopen-2.so"
+#define LIB_DLOPEN_NODELETE_DEP  "lib-tst-dl_mseal-dlopen-2-1.so"
+
+static int
+new_flags (const char flags[4])
+{
+  bool read_flag  = flags[0] == 'r';
+  bool write_flag = flags[1] == 'w';
+  bool exec_flag  = flags[2] == 'x';
+
+  write_flag = !write_flag;
+
+  return (read_flag ? PROT_READ : 0)
+	 | (write_flag ? PROT_WRITE : 0)
+	 | (exec_flag ? PROT_EXEC : 0);
+}
+
+/* Expected libraries that loader will seal.  */
+static const char *expected_sealed_libs[] =
+{
+#ifdef TEST_STATIC
+  "tst-dl_mseal-static",
+#else
+  "libc.so",
+  "ld.so",
+  "tst-dl_mseal",
+  LIB_PRELOAD,
+  LIB_NEEDED_1,
+  LIB_NEEDED_2,
+  LIB_DLOPEN_NODELETE,
+  LIB_DLOPEN_NODELETE_DEP,
+#endif
+  "[vdso]",
+};
+
+/* Libraries/VMA that could not be sealed.  */
+static const char *non_sealed_vmas[] =
+{
+  ".",				/* basename value for empty string anonymous
+				   mappings.  */
+  "[heap]",
+  "[vsyscall]",
+  "[vvar]",
+  "[stack]",
+  "zero",			/* /dev/zero  */
+#ifndef TEST_STATIC
+  "tst-dl_mseal-mod-2.so",
+  LIB_DLOPEN_DEFAULT,
+  LIB_DLOPEN_DEFAULT_DEP
+#endif
+};
+
+static int
+is_in_string_list (const char *s, const char *const list[], size_t len)
+{
+  for (size_t i = 0; i != len; i++)
+    if (strcmp (s, list[i]) == 0)
+      return i;
+  return -1;
+}
+
+static int
+handle_restart (void)
+{
+#ifndef TEST_STATIC
+  xdlopen (LIB_DLOPEN_NODELETE, RTLD_NOW | RTLD_NODELETE);
+  xdlopen (LIB_DLOPEN_DEFAULT, RTLD_NOW);
+#endif
+
+  FILE *fp = xfopen ("/proc/self/maps", "r");
+  char *line = NULL;
+  size_t linesiz = 0;
+
+  unsigned long pagesize = getpagesize ();
+
+  bool found_expected[array_length(expected_sealed_libs)] = { false };
+  while (xgetline (&line, &linesiz, fp) > 0)
+    {
+      uintptr_t start;
+      uintptr_t end;
+      char flags[5] = { 0 };
+      char name[256] = { 0 };
+      int idx;
+
+      /* The line is in the form:
+	 start-end flags offset dev inode pathname   */
+      int r = sscanf (line,
+		      "%" SCNxPTR "-%" SCNxPTR " %4s %*s %*s %*s %256s",
+		      &start,
+		      &end,
+		      flags,
+		      name);
+      TEST_VERIFY_EXIT (r == 3 || r == 4);
+
+      int found = false;
+
+      const char *libname = basename (name);
+      if ((idx = is_in_string_list (libname, expected_sealed_libs,
+				    array_length (expected_sealed_libs)))
+	   != -1)
+	{
+	  /* Check if we can change the protection flags of the segment.  */
+	  int new_prot = new_flags (flags);
+	  TEST_VERIFY_EXIT (mprotect ((void *) start, end - start,
+				      new_prot) == -1);
+	  TEST_VERIFY_EXIT (errno == EPERM);
+
+	  /* Also checks trying to map over the sealed libraries.  */
+	  {
+	    char *p = mmap ((void *) start, pagesize, new_prot,
+			    MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+	    TEST_VERIFY_EXIT (p == MAP_FAILED);
+	    TEST_VERIFY_EXIT (errno == EPERM);
+	  }
+
+	  /* And if remap is also blocked.  */
+	  {
+	    char *p = mremap ((void *) start, end - start, end - start, 0);
+	    TEST_VERIFY_EXIT (p == MAP_FAILED);
+	    TEST_VERIFY_EXIT (errno == EPERM);
+	  }
+
+	  printf ("sealed:     vma: %#" PRIxPTR "-%#" PRIxPTR " %s %s\n",
+		  start,
+		  end,
+		  flags,
+		  name);
+
+	  found_expected[idx] = true;
+	  found = true;
+	}
+
+      if (!found)
+	{
+	  if (is_in_string_list (libname, non_sealed_vmas,
+				 array_length (non_sealed_vmas)) != -1)
+	    printf ("not-sealed: vma: %#" PRIxPTR "-%#" PRIxPTR " %s %s\n",
+		    start,
+		    end,
+		    flags,
+		    name);
+	  else
+	    FAIL_EXIT1 ("unexpected vma: %#" PRIxPTR "-%#" PRIxPTR " %s %s\n",
+			start,
+			end,
+			flags,
+			name);
+	}
+    }
+  xfclose (fp);
+
+  printf ("\n");
+
+  /* Also check if all the expected sealed maps were found.  */
+  for (int i = 0; i < array_length (expected_sealed_libs); i++)
+    if (!found_expected[i])
+      FAIL_EXIT1 ("expected VMA %s not sealed\n", expected_sealed_libs[i]);
+
+  return 0;
+}
+
+static int restart;
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+
+static int
+do_test (int argc, char *argv[])
+{
+  /* We must have either:
+     - One or four parameters left if called initially:
+       + path to ld.so         optional
+       + "--library-path"      optional
+       + the library path      optional
+       + the application name  */
+  if (restart)
+    return handle_restart ();
+
+  /* Check the test requirements.  */
+  {
+    int r = mseal (NULL, 0, 0);
+    if (r == -1 && errno == ENOSYS)
+      FAIL_UNSUPPORTED ("mseal is not supported by the kernel");
+    else
+      TEST_VERIFY_EXIT (r == 0);
+  }
+  support_need_proc ("Reads /proc/self/maps to get stack names.");
+
+  char *spargv[9];
+  int i = 0;
+  for (; i < argc - 1; i++)
+    spargv[i] = argv[i + 1];
+  spargv[i++] = (char *) "--direct";
+  spargv[i++] = (char *) "--restart";
+  spargv[i] = NULL;
+
+  char *envvarss[3];
+  envvarss[0] = (char *) "GLIBC_TUNABLES=glibc.rtld.seal=2";
+#ifndef TEST_STATIC
+  envvarss[1] = (char *) "LD_PRELOAD=" LIB_PRELOAD;
+  envvarss[2] = NULL;
+#else
+  envvarss[1] = NULL;
+#endif
+
+  struct support_capture_subprocess result =
+    support_capture_subprogram (spargv[0], spargv, envvarss);
+  support_capture_subprocess_check (&result, "tst-dl_mseal", 0,
+				    sc_allow_stdout);
+
+  {
+    FILE *out = fmemopen (result.out.buffer, result.out.length, "r");
+    TEST_VERIFY (out != NULL);
+    char *line = NULL;
+    size_t linesz = 0;
+    while (xgetline (&line, &linesz, out))
+      printf ("%s", line);
+    fclose (out);
+  }
+
+  support_capture_subprocess_free (&result);
+
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [glibc/azanella/mseal] elf: Add support to memory sealing
@ 2024-06-04 22:57 Adhemerval Zanella
  0 siblings, 0 replies; 5+ messages in thread
From: Adhemerval Zanella @ 2024-06-04 22:57 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=8bcdfd9bc3cacaead44d68791731889a13d134c1

commit 8bcdfd9bc3cacaead44d68791731889a13d134c1
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Sun Jun 2 20:31:27 2024 -0300

    elf: Add support to memory sealing
    
    It uses the new Linux mseal syscall to seal memory mappings to avoid
    further changes (such changing the memory protection or remap).  The
    sealing is done in multiple places where the memory is supported to
    be immuatable over program execution:
    
      * All DT_NEEDED dependencies from the binary, including the RELRO
        seguments after PT_GNU_RELRO setup.
    
      * The binary itself, including dynamic and static linked.  In both
        cases it is up either to binary or the loader to setup the
        sealing.
    
      * The ld.so.cache cache.
    
      * The vDSO segment (if existent).
    
      * Any preload libraries.
    
    For binary dependencies, the RTLD_NODELETE is used to signal the
    link_map should be sealed.  It also make dlopen objects with the
    flag sealed as well.
    
    The sealing is controlled by a new tunable, glibc.rtld.seal, with
    three different states:
    
      0. Disabled  where no sealing is done.  This is the default.
    
      1. Enabled, where loader will issue the mseal syscall on the
         memory mappings but any failure will be ignored.
    
      2. Enforce, similar to Enabled but any failure from the mseal
         will terminate the process.
    
    Checked on x86_64-linux-gnu.

Diff:
---
 elf/dl-cache.c                     |  5 +++-
 elf/dl-load.c                      |  2 ++
 elf/dl-mseal-mode.h                | 29 ++++++++++++++++++++++
 elf/dl-reloc.c                     | 33 ++++++++++++++++++++++++
 elf/dl-support.c                   |  7 ++++++
 elf/dl-tunables.list               |  5 ++++
 elf/rtld.c                         | 10 +++++---
 elf/setup-vdso.h                   |  3 +++
 elf/tst-rtld-list-tunables.exp     |  1 +
 include/link.h                     |  1 +
 string/strerrorname_np.c           |  1 +
 sysdeps/generic/dl-mseal.h         | 25 +++++++++++++++++++
 sysdeps/generic/ldsodefs.h         |  4 +++
 sysdeps/unix/sysv/linux/Makefile   |  4 +++
 sysdeps/unix/sysv/linux/dl-mseal.c | 51 ++++++++++++++++++++++++++++++++++++++
 sysdeps/unix/sysv/linux/dl-mseal.h | 29 ++++++++++++++++++++++
 16 files changed, 206 insertions(+), 4 deletions(-)

diff --git a/elf/dl-cache.c b/elf/dl-cache.c
index 85f3f179ed..d8a494a52a 100644
--- a/elf/dl-cache.c
+++ b/elf/dl-cache.c
@@ -26,6 +26,7 @@
 #include <_itoa.h>
 #include <dl-hwcaps.h>
 #include <dl-isa-level.h>
+#include <dl-mseal.h>
 
 #ifndef _DL_PLATFORMS_COUNT
 # define _DL_PLATFORMS_COUNT 0
@@ -410,7 +411,6 @@ _dl_load_cache_lookup (const char *name)
       /* Read the contents of the file.  */
       void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,
 					       PROT_READ);
-
       /* We can handle three different cache file formats here:
 	 - only the new format
 	 - the old libc5/glibc2.0/2.1 format
@@ -471,6 +471,9 @@ _dl_load_cache_lookup (const char *name)
 	  cache = (void *) -1;
 	}
 
+      if (cache != (void *) -1)
+	_dl_mseal (file, cachesize);
+
       assert (cache != NULL);
     }
 
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 8a89b71016..7d8824ea89 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1431,6 +1431,8 @@ cannot enable executable stack as shared object requires");
     /* Assign the next available module ID.  */
     _dl_assign_tls_modid (l);
 
+  l->l_seal = mode & RTLD_NODELETE ? 1 : 0;
+
 #ifdef DL_AFTER_LOAD
   DL_AFTER_LOAD (l);
 #endif
diff --git a/elf/dl-mseal-mode.h b/elf/dl-mseal-mode.h
new file mode 100644
index 0000000000..7f9ede4db7
--- /dev/null
+++ b/elf/dl-mseal-mode.h
@@ -0,0 +1,29 @@
+/* Memory sealing.  Generic definitions.
+   Copyright (C) 2024 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/>.  */
+
+#ifndef _DL_SEAL_MODE_H
+#define _DL_SEAL_MODE_H
+
+enum dl_seal_mode
+{
+  DL_SEAL_DISABLE = 0,
+  DL_SEAL_ENABLE = 1,
+  DL_SEAL_ENFORCE = 2,
+};
+
+#endif
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 4bf7aec88b..f0bde68782 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -28,6 +28,7 @@
 #include <_itoa.h>
 #include <libc-pointer-arith.h>
 #include "dynamic-link.h"
+#include <dl-mseal.h>
 
 /* Statistics function.  */
 #ifdef SHARED
@@ -347,6 +348,11 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
      done, do it.  */
   if (l->l_relro_size != 0)
     _dl_protect_relro (l);
+
+  /* Seal the memory mapping after RELRO setup, we can use the PT_LOAD
+     segments because even if relro splits the the original RW VMA,
+     mseal works with multiple VMAs with different flags.  */
+  _dl_mseal_map (l);
 }
 
 
@@ -369,6 +375,33 @@ cannot apply additional memory protection after relocation");
     }
 }
 
+void
+_dl_mseal_map (const struct link_map *l)
+{
+  if (l->l_seal)
+    {
+      if (l->l_contiguous)
+	_dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
+      else
+	{
+	  for (const ElfW(Phdr) *ph = l->l_phdr;
+	       ph < &l->l_phdr[l->l_phnum];
+	       ++ph)
+	    switch (ph->p_type)
+	      {
+	      case PT_LOAD:
+		{
+		  ElfW(Addr) mapstart = l->l_addr
+		      + (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1));
+		  ElfW(Addr) allocend = l->l_addr + ph->p_vaddr + ph->p_memsz;
+		  _dl_mseal ((void *) mapstart, allocend - mapstart);
+		}
+	        break;
+	    }
+	}
+    }
+}
+
 void
 __attribute_noinline__
 _dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt)
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 451932dd03..a206049ce3 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -45,6 +45,7 @@
 #include <dl-find_object.h>
 #include <array_length.h>
 #include <dl-symbol-redir-ifunc.h>
+#include <dl-mseal.h>
 
 extern char *__progname;
 char **_dl_argv = &__progname;	/* This is checked for some error messages.  */
@@ -99,6 +100,7 @@ static struct link_map _dl_main_map =
     .l_used = 1,
     .l_tls_offset = NO_TLS_OFFSET,
     .l_serial = 1,
+    .l_seal = SUPPORT_MSEAL,
   };
 
 /* Namespace information.  */
@@ -340,6 +342,11 @@ _dl_non_dynamic_init (void)
   /* Setup relro on the binary itself.  */
   if (_dl_main_map.l_relro_size != 0)
     _dl_protect_relro (&_dl_main_map);
+
+  /* Seal the memory mapping after RELRO setup, we can use the PT_LOAD
+     segments because even if relro splits the the original RW VMA,
+     mseal works with multiple VMAs with different flags.  */
+  _dl_mseal_map (&_dl_main_map);
 }
 
 #ifdef DL_SYSINFO_IMPLEMENTATION
diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index 1186272c81..ba72b3152d 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -142,6 +142,11 @@ glibc {
       maxval: 1
       default: 0
     }
+    seal {
+      type: INT_32
+      minval: 0
+      maxval: 2
+    }
   }
 
   mem {
diff --git a/elf/rtld.c b/elf/rtld.c
index e9525ea987..174389e205 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -53,6 +53,7 @@
 #include <dl-find_object.h>
 #include <dl-audit-check.h>
 #include <dl-call_tls_init_tp.h>
+#include <dl-mseal.h>
 
 #include <assert.h>
 
@@ -477,6 +478,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
   GL(dl_rtld_map).l_real = &GL(dl_rtld_map);
   GL(dl_rtld_map).l_map_start = (ElfW(Addr)) &__ehdr_start;
   GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;
+  GL(dl_rtld_map).l_seal = 1;
   /* Copy the TLS related data if necessary.  */
 #ifndef DONT_USE_BOOTSTRAP_MAP
 # if NO_TLS_OFFSET != 0
@@ -809,7 +811,8 @@ do_preload (const char *fname, struct link_map *main_map, const char *where)
 
   args.str = fname;
   args.loader = main_map;
-  args.mode = __RTLD_SECURE;
+  /* RTLD_NODELETE enables sealing.  */
+  args.mode = __RTLD_SECURE | RTLD_NODELETE;
 
   unsigned int old_nloaded = GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
 
@@ -1123,6 +1126,7 @@ rtld_setup_main_map (struct link_map *main_map)
   /* And it was opened directly.  */
   ++main_map->l_direct_opencount;
   main_map->l_contiguous = 1;
+  main_map->l_seal = 1;
 
   /* A PT_LOAD segment at an unexpected address will clear the
      l_contiguous flag.  The ELF specification says that PT_LOAD
@@ -1636,7 +1640,7 @@ dl_main (const ElfW(Phdr) *phdr,
       /* Create a link_map for the executable itself.
 	 This will be what dlopen on "" returns.  */
       main_map = _dl_new_object ((char *) "", "", lt_executable, NULL,
-				 __RTLD_OPENEXEC, LM_ID_BASE);
+				 __RTLD_OPENEXEC | RTLD_NODELETE, LM_ID_BASE);
       assert (main_map != NULL);
       main_map->l_phdr = phdr;
       main_map->l_phnum = phnum;
@@ -1964,7 +1968,7 @@ dl_main (const ElfW(Phdr) *phdr,
     RTLD_TIMING_VAR (start);
     rtld_timer_start (&start);
     _dl_map_object_deps (main_map, preloads, npreloads,
-			 state.mode == rtld_mode_trace, 0);
+			 state.mode == rtld_mode_trace, RTLD_NODELETE);
     rtld_timer_accum (&load_time, start);
   }
 
diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h
index 888e1e4897..fe5b1928c3 100644
--- a/elf/setup-vdso.h
+++ b/elf/setup-vdso.h
@@ -66,6 +66,7 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
 
       /* The vDSO is always used.  */
       l->l_used = 1;
+      l->l_seal = 1;
 
       /* Initialize l_local_scope to contain just this map.  This allows
 	 the use of dl_lookup_symbol_x to resolve symbols within the vdso.
@@ -104,6 +105,8 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
       if (GLRO(dl_sysinfo) == DL_SYSINFO_DEFAULT)
 	GLRO(dl_sysinfo) = GLRO(dl_sysinfo_dso)->e_entry + l->l_addr;
 # endif
+
+      _dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
     }
 #endif
 }
diff --git a/elf/tst-rtld-list-tunables.exp b/elf/tst-rtld-list-tunables.exp
index db0e1c86e9..6f1044c480 100644
--- a/elf/tst-rtld-list-tunables.exp
+++ b/elf/tst-rtld-list-tunables.exp
@@ -15,3 +15,4 @@ glibc.rtld.dynamic_sort: 2 (min: 1, max: 2)
 glibc.rtld.enable_secure: 0 (min: 0, max: 1)
 glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
 glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0x[f]+)
+glibc.rtld.seal: 0 (min: 0, max: 2)
diff --git a/include/link.h b/include/link.h
index cb0d7d8e2f..b7c19f61da 100644
--- a/include/link.h
+++ b/include/link.h
@@ -212,6 +212,7 @@ struct link_map
     unsigned int l_find_object_processed:1; /* Zero if _dl_find_object_update
 					       needs to process this
 					       lt_library map.  */
+    unsigned int l_seal:1;
 
     /* NODELETE status of the map.  Only valid for maps of type
        lt_loaded.  Lazy binding sets l_nodelete_active directly,
diff --git a/string/strerrorname_np.c b/string/strerrorname_np.c
index 042cea381c..e0e22fa79e 100644
--- a/string/strerrorname_np.c
+++ b/string/strerrorname_np.c
@@ -17,6 +17,7 @@
    <https://www.gnu.org/licenses/>.  */
 
 #include <stdio.h>
+#include <string.h>
 
 const char *
 strerrorname_np (int errnum)
diff --git a/sysdeps/generic/dl-mseal.h b/sysdeps/generic/dl-mseal.h
new file mode 100644
index 0000000000..a5399e4d5c
--- /dev/null
+++ b/sysdeps/generic/dl-mseal.h
@@ -0,0 +1,25 @@
+/* Memory sealing.  Generic version.
+   Copyright (C) 2024 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/>.  */
+
+static inline int
+_dl_mseal (void *addr, size_t len)
+{
+  return 0;
+}
+
+#define SUPPORT_MSEAL 0
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 50f58a60e3..7ad747acf4 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1017,6 +1017,10 @@ extern void _dl_relocate_object (struct link_map *map,
 /* Protect PT_GNU_RELRO area.  */
 extern void _dl_protect_relro (struct link_map *map) attribute_hidden;
 
+/* Scan all PT_LOAD entries and seal the VMA range to avoid further
+   changes.  */
+extern void _dl_mseal_map (const struct link_map *map) attribute_hidden;
+
 /* Call _dl_signal_error with a message about an unhandled reloc type.
    TYPE is the result of ELFW(R_TYPE) (r_info), i.e. an R_<CPU>_* value.
    PLT is nonzero if this was a PLT reloc; it just affects the message.  */
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 82d523e588..8c181d46dc 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -625,6 +625,10 @@ sysdep-rtld-routines += \
   dl-sbrk \
   # sysdep-rtld-routines
 
+dl-routines += \
+  dl-mseal \
+  # dl-routines
+
 others += \
   pldd \
   # others
diff --git a/sysdeps/unix/sysv/linux/dl-mseal.c b/sysdeps/unix/sysv/linux/dl-mseal.c
new file mode 100644
index 0000000000..1313fa3868
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.c
@@ -0,0 +1,51 @@
+/* Memory sealing.  Linux version.
+   Copyright (C) 2024 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 <atomic.h>
+#include <dl-mseal.h>
+#include <dl-mseal-mode.h>
+#include <dl-tunables.h>
+#include <ldsodefs.h>
+
+int
+_dl_mseal (void *addr, size_t len)
+{
+  int32_t mode = TUNABLE_GET (glibc, rtld, seal, int32_t, NULL);
+  if (mode == DL_SEAL_DISABLE)
+    return 0;
+
+  int r;
+#if __ASSUME_MSEAL
+  r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0);
+#else
+  r = -ENOSYS;
+  static int mseal_supported = true;
+  if (atomic_load_relaxed (&mseal_supported))
+    {
+      r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0);
+      if (r == -ENOSYS)
+	atomic_store_relaxed (&mseal_supported, false);
+    }
+#endif
+  if (mode == DL_SEAL_ENFORCE && r != 0)
+    _dl_fatal_printf ("Fatal error: sealing is enforced and kernel has "
+		      "failed for 0x%lx-0x%lx range\n",
+		      (long unsigned int) addr,
+		      (long unsigned int) addr + len);
+  return r;
+}
diff --git a/sysdeps/unix/sysv/linux/dl-mseal.h b/sysdeps/unix/sysv/linux/dl-mseal.h
new file mode 100644
index 0000000000..f56d33d97c
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.h
@@ -0,0 +1,29 @@
+/* Memory sealing.  Linux version.
+   Copyright (C) 2024 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/>.  */
+
+/* Seal the ADDR or size LEN to protect against modifications, such as
+   changes on the permission flags (through mprotect), remap (through
+   mmap and/or remap), shrink, destruction changes (madvise with
+   MADV_DONTNEED), or change its size.  The input has the same constraints
+   as the mseal syscall.
+
+   Return 0 in case of success or a negative value otherwise (a negative
+   errno).  */
+int _dl_mseal (void *addr, size_t len) attribute_hidden;
+
+#define SUPPORT_MSEAL 1

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [glibc/azanella/mseal] elf: Add support to memory sealing
@ 2024-06-03 21:51 Adhemerval Zanella
  0 siblings, 0 replies; 5+ messages in thread
From: Adhemerval Zanella @ 2024-06-03 21:51 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=63c15758364a3e5f6fd0446491ff5b7b3e335b3b

commit 63c15758364a3e5f6fd0446491ff5b7b3e335b3b
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Sun Jun 2 20:31:27 2024 -0300

    elf: Add support to memory sealing
    
    It uses the new Linux mseal syscall to seal memory mappings to avoid
    further changes (such changing the memory protection or remap).  The
    sealing is done in multiple places where the memory is supported to
    be immuatable over program execution:
    
      * All DT_NEEDED dependencies from the binary, including the RELRO
        seguments after PT_GNU_RELRO setup.
    
      * The binary itself, including dynamic and static linked.  In both
        cases it is up either to binary or the loader to setup the
        sealing.
    
      * The ld.so.cache cache.
    
      * The vDSO segment (if existent).
    
    For binary dependencies, the RTLD_NODELETE is used to signal the
    link_map should be sealed.  It also make dlopen objects with the
    flag sealed as well.
    
    The sealing is controlled by a new tunable, glibc.rtld.seal, with
    three different states:
    
      0. Disabled  where no sealing is done.  This is the default.
    
      1. Enabled, where loader will issue the mseal syscall on the
         memory mappings but any failure will be ignored.
    
      2. Enforce, similar to Enabled but any failure from the mseal
         will terminate the process.
    
    Checked on x86_64-linux-gnu.

Diff:
---
 elf/dl-cache.c                     |  3 +++
 elf/dl-load.c                      |  2 ++
 elf/dl-mseal-mode.h                | 29 ++++++++++++++++++++++
 elf/dl-reloc.c                     | 33 ++++++++++++++++++++++++
 elf/dl-support.c                   |  7 ++++++
 elf/dl-tunables.list               |  5 ++++
 elf/rtld.c                         |  7 ++++--
 elf/setup-vdso.h                   |  3 +++
 elf/tst-rtld-list-tunables.exp     |  1 +
 include/link.h                     |  1 +
 string/strerrorname_np.c           |  1 +
 sysdeps/generic/dl-mseal.h         | 25 +++++++++++++++++++
 sysdeps/generic/ldsodefs.h         |  4 +++
 sysdeps/unix/sysv/linux/Makefile   |  4 +++
 sysdeps/unix/sysv/linux/dl-mseal.c | 51 ++++++++++++++++++++++++++++++++++++++
 sysdeps/unix/sysv/linux/dl-mseal.h | 29 ++++++++++++++++++++++
 16 files changed, 203 insertions(+), 2 deletions(-)

diff --git a/elf/dl-cache.c b/elf/dl-cache.c
index 85f3f179ed..1770c90194 100644
--- a/elf/dl-cache.c
+++ b/elf/dl-cache.c
@@ -26,6 +26,7 @@
 #include <_itoa.h>
 #include <dl-hwcaps.h>
 #include <dl-isa-level.h>
+#include <dl-mseal.h>
 
 #ifndef _DL_PLATFORMS_COUNT
 # define _DL_PLATFORMS_COUNT 0
@@ -410,6 +411,8 @@ _dl_load_cache_lookup (const char *name)
       /* Read the contents of the file.  */
       void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,
 					       PROT_READ);
+      if (cache != MAP_FAILED)
+	_dl_mseal (file, cachesize);
 
       /* We can handle three different cache file formats here:
 	 - only the new format
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 8a89b71016..7d8824ea89 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1431,6 +1431,8 @@ cannot enable executable stack as shared object requires");
     /* Assign the next available module ID.  */
     _dl_assign_tls_modid (l);
 
+  l->l_seal = mode & RTLD_NODELETE ? 1 : 0;
+
 #ifdef DL_AFTER_LOAD
   DL_AFTER_LOAD (l);
 #endif
diff --git a/elf/dl-mseal-mode.h b/elf/dl-mseal-mode.h
new file mode 100644
index 0000000000..7f9ede4db7
--- /dev/null
+++ b/elf/dl-mseal-mode.h
@@ -0,0 +1,29 @@
+/* Memory sealing.  Generic definitions.
+   Copyright (C) 2024 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/>.  */
+
+#ifndef _DL_SEAL_MODE_H
+#define _DL_SEAL_MODE_H
+
+enum dl_seal_mode
+{
+  DL_SEAL_DISABLE = 0,
+  DL_SEAL_ENABLE = 1,
+  DL_SEAL_ENFORCE = 2,
+};
+
+#endif
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 4bf7aec88b..f0bde68782 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -28,6 +28,7 @@
 #include <_itoa.h>
 #include <libc-pointer-arith.h>
 #include "dynamic-link.h"
+#include <dl-mseal.h>
 
 /* Statistics function.  */
 #ifdef SHARED
@@ -347,6 +348,11 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
      done, do it.  */
   if (l->l_relro_size != 0)
     _dl_protect_relro (l);
+
+  /* Seal the memory mapping after RELRO setup, we can use the PT_LOAD
+     segments because even if relro splits the the original RW VMA,
+     mseal works with multiple VMAs with different flags.  */
+  _dl_mseal_map (l);
 }
 
 
@@ -369,6 +375,33 @@ cannot apply additional memory protection after relocation");
     }
 }
 
+void
+_dl_mseal_map (const struct link_map *l)
+{
+  if (l->l_seal)
+    {
+      if (l->l_contiguous)
+	_dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
+      else
+	{
+	  for (const ElfW(Phdr) *ph = l->l_phdr;
+	       ph < &l->l_phdr[l->l_phnum];
+	       ++ph)
+	    switch (ph->p_type)
+	      {
+	      case PT_LOAD:
+		{
+		  ElfW(Addr) mapstart = l->l_addr
+		      + (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1));
+		  ElfW(Addr) allocend = l->l_addr + ph->p_vaddr + ph->p_memsz;
+		  _dl_mseal ((void *) mapstart, allocend - mapstart);
+		}
+	        break;
+	    }
+	}
+    }
+}
+
 void
 __attribute_noinline__
 _dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt)
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 451932dd03..a206049ce3 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -45,6 +45,7 @@
 #include <dl-find_object.h>
 #include <array_length.h>
 #include <dl-symbol-redir-ifunc.h>
+#include <dl-mseal.h>
 
 extern char *__progname;
 char **_dl_argv = &__progname;	/* This is checked for some error messages.  */
@@ -99,6 +100,7 @@ static struct link_map _dl_main_map =
     .l_used = 1,
     .l_tls_offset = NO_TLS_OFFSET,
     .l_serial = 1,
+    .l_seal = SUPPORT_MSEAL,
   };
 
 /* Namespace information.  */
@@ -340,6 +342,11 @@ _dl_non_dynamic_init (void)
   /* Setup relro on the binary itself.  */
   if (_dl_main_map.l_relro_size != 0)
     _dl_protect_relro (&_dl_main_map);
+
+  /* Seal the memory mapping after RELRO setup, we can use the PT_LOAD
+     segments because even if relro splits the the original RW VMA,
+     mseal works with multiple VMAs with different flags.  */
+  _dl_mseal_map (&_dl_main_map);
 }
 
 #ifdef DL_SYSINFO_IMPLEMENTATION
diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index 1186272c81..ba72b3152d 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -142,6 +142,11 @@ glibc {
       maxval: 1
       default: 0
     }
+    seal {
+      type: INT_32
+      minval: 0
+      maxval: 2
+    }
   }
 
   mem {
diff --git a/elf/rtld.c b/elf/rtld.c
index e9525ea987..2dc257e0dc 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -53,6 +53,7 @@
 #include <dl-find_object.h>
 #include <dl-audit-check.h>
 #include <dl-call_tls_init_tp.h>
+#include <dl-mseal.h>
 
 #include <assert.h>
 
@@ -477,6 +478,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
   GL(dl_rtld_map).l_real = &GL(dl_rtld_map);
   GL(dl_rtld_map).l_map_start = (ElfW(Addr)) &__ehdr_start;
   GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;
+  GL(dl_rtld_map).l_seal = 1;
   /* Copy the TLS related data if necessary.  */
 #ifndef DONT_USE_BOOTSTRAP_MAP
 # if NO_TLS_OFFSET != 0
@@ -1123,6 +1125,7 @@ rtld_setup_main_map (struct link_map *main_map)
   /* And it was opened directly.  */
   ++main_map->l_direct_opencount;
   main_map->l_contiguous = 1;
+  main_map->l_seal = 1;
 
   /* A PT_LOAD segment at an unexpected address will clear the
      l_contiguous flag.  The ELF specification says that PT_LOAD
@@ -1636,7 +1639,7 @@ dl_main (const ElfW(Phdr) *phdr,
       /* Create a link_map for the executable itself.
 	 This will be what dlopen on "" returns.  */
       main_map = _dl_new_object ((char *) "", "", lt_executable, NULL,
-				 __RTLD_OPENEXEC, LM_ID_BASE);
+				 __RTLD_OPENEXEC | RTLD_NODELETE, LM_ID_BASE);
       assert (main_map != NULL);
       main_map->l_phdr = phdr;
       main_map->l_phnum = phnum;
@@ -1964,7 +1967,7 @@ dl_main (const ElfW(Phdr) *phdr,
     RTLD_TIMING_VAR (start);
     rtld_timer_start (&start);
     _dl_map_object_deps (main_map, preloads, npreloads,
-			 state.mode == rtld_mode_trace, 0);
+			 state.mode == rtld_mode_trace, RTLD_NODELETE);
     rtld_timer_accum (&load_time, start);
   }
 
diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h
index 888e1e4897..fe5b1928c3 100644
--- a/elf/setup-vdso.h
+++ b/elf/setup-vdso.h
@@ -66,6 +66,7 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
 
       /* The vDSO is always used.  */
       l->l_used = 1;
+      l->l_seal = 1;
 
       /* Initialize l_local_scope to contain just this map.  This allows
 	 the use of dl_lookup_symbol_x to resolve symbols within the vdso.
@@ -104,6 +105,8 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
       if (GLRO(dl_sysinfo) == DL_SYSINFO_DEFAULT)
 	GLRO(dl_sysinfo) = GLRO(dl_sysinfo_dso)->e_entry + l->l_addr;
 # endif
+
+      _dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
     }
 #endif
 }
diff --git a/elf/tst-rtld-list-tunables.exp b/elf/tst-rtld-list-tunables.exp
index db0e1c86e9..6f1044c480 100644
--- a/elf/tst-rtld-list-tunables.exp
+++ b/elf/tst-rtld-list-tunables.exp
@@ -15,3 +15,4 @@ glibc.rtld.dynamic_sort: 2 (min: 1, max: 2)
 glibc.rtld.enable_secure: 0 (min: 0, max: 1)
 glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
 glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0x[f]+)
+glibc.rtld.seal: 0 (min: 0, max: 2)
diff --git a/include/link.h b/include/link.h
index cb0d7d8e2f..b7c19f61da 100644
--- a/include/link.h
+++ b/include/link.h
@@ -212,6 +212,7 @@ struct link_map
     unsigned int l_find_object_processed:1; /* Zero if _dl_find_object_update
 					       needs to process this
 					       lt_library map.  */
+    unsigned int l_seal:1;
 
     /* NODELETE status of the map.  Only valid for maps of type
        lt_loaded.  Lazy binding sets l_nodelete_active directly,
diff --git a/string/strerrorname_np.c b/string/strerrorname_np.c
index 042cea381c..e0e22fa79e 100644
--- a/string/strerrorname_np.c
+++ b/string/strerrorname_np.c
@@ -17,6 +17,7 @@
    <https://www.gnu.org/licenses/>.  */
 
 #include <stdio.h>
+#include <string.h>
 
 const char *
 strerrorname_np (int errnum)
diff --git a/sysdeps/generic/dl-mseal.h b/sysdeps/generic/dl-mseal.h
new file mode 100644
index 0000000000..a5399e4d5c
--- /dev/null
+++ b/sysdeps/generic/dl-mseal.h
@@ -0,0 +1,25 @@
+/* Memory sealing.  Generic version.
+   Copyright (C) 2024 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/>.  */
+
+static inline int
+_dl_mseal (void *addr, size_t len)
+{
+  return 0;
+}
+
+#define SUPPORT_MSEAL 0
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 50f58a60e3..7ad747acf4 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1017,6 +1017,10 @@ extern void _dl_relocate_object (struct link_map *map,
 /* Protect PT_GNU_RELRO area.  */
 extern void _dl_protect_relro (struct link_map *map) attribute_hidden;
 
+/* Scan all PT_LOAD entries and seal the VMA range to avoid further
+   changes.  */
+extern void _dl_mseal_map (const struct link_map *map) attribute_hidden;
+
 /* Call _dl_signal_error with a message about an unhandled reloc type.
    TYPE is the result of ELFW(R_TYPE) (r_info), i.e. an R_<CPU>_* value.
    PLT is nonzero if this was a PLT reloc; it just affects the message.  */
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 82d523e588..8c181d46dc 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -625,6 +625,10 @@ sysdep-rtld-routines += \
   dl-sbrk \
   # sysdep-rtld-routines
 
+dl-routines += \
+  dl-mseal \
+  # dl-routines
+
 others += \
   pldd \
   # others
diff --git a/sysdeps/unix/sysv/linux/dl-mseal.c b/sysdeps/unix/sysv/linux/dl-mseal.c
new file mode 100644
index 0000000000..1313fa3868
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.c
@@ -0,0 +1,51 @@
+/* Memory sealing.  Linux version.
+   Copyright (C) 2024 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 <atomic.h>
+#include <dl-mseal.h>
+#include <dl-mseal-mode.h>
+#include <dl-tunables.h>
+#include <ldsodefs.h>
+
+int
+_dl_mseal (void *addr, size_t len)
+{
+  int32_t mode = TUNABLE_GET (glibc, rtld, seal, int32_t, NULL);
+  if (mode == DL_SEAL_DISABLE)
+    return 0;
+
+  int r;
+#if __ASSUME_MSEAL
+  r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0);
+#else
+  r = -ENOSYS;
+  static int mseal_supported = true;
+  if (atomic_load_relaxed (&mseal_supported))
+    {
+      r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0);
+      if (r == -ENOSYS)
+	atomic_store_relaxed (&mseal_supported, false);
+    }
+#endif
+  if (mode == DL_SEAL_ENFORCE && r != 0)
+    _dl_fatal_printf ("Fatal error: sealing is enforced and kernel has "
+		      "failed for 0x%lx-0x%lx range\n",
+		      (long unsigned int) addr,
+		      (long unsigned int) addr + len);
+  return r;
+}
diff --git a/sysdeps/unix/sysv/linux/dl-mseal.h b/sysdeps/unix/sysv/linux/dl-mseal.h
new file mode 100644
index 0000000000..f56d33d97c
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.h
@@ -0,0 +1,29 @@
+/* Memory sealing.  Linux version.
+   Copyright (C) 2024 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/>.  */
+
+/* Seal the ADDR or size LEN to protect against modifications, such as
+   changes on the permission flags (through mprotect), remap (through
+   mmap and/or remap), shrink, destruction changes (madvise with
+   MADV_DONTNEED), or change its size.  The input has the same constraints
+   as the mseal syscall.
+
+   Return 0 in case of success or a negative value otherwise (a negative
+   errno).  */
+int _dl_mseal (void *addr, size_t len) attribute_hidden;
+
+#define SUPPORT_MSEAL 1

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2024-06-21 14:30 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-06-04 14:08 [glibc/azanella/mseal] elf: Add support to memory sealing Adhemerval Zanella
  -- strict thread matches above, loose matches on Subject: below --
2024-06-21 14:30 Adhemerval Zanella
2024-06-19 13:04 Adhemerval Zanella
2024-06-04 22:57 Adhemerval Zanella
2024-06-03 21:51 Adhemerval Zanella

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).