diff mbox series

[v4,6/9] elf: Add support to memory sealing

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

Commit Message

Adhemerval Zanella Dec. 6, 2024, 5:37 p.m. UTC
The new Linux mseal syscall allows mark a memory mapping to avoid
further changes (such as chaing the protection flags).  The memory
sealing is done in multiple places where the memory is supposed to
be immutable during 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 linked ones.  In
    both cases, it is up either to binary or the loader to set up the
    sealing.

  * Any preload libraries, including depedencies.

  * Any library loaded with dlopen with RTLD_NODELETE flag.

  * Audit modules.

  * The loader bump allocator.

The memory sealing is controled by a new gnu attribute,
GNU_PROPERTY_MEMORY_SEAL, added by the new static linker option
'-z memory-seal'.  It is set per binary, including statically linked
and shared objects.

Checked on x86_64-linux-gnu and aarch64-linux-gnu.
---
 NEWS                               |  6 ++++
 elf/dl-load.c                      |  4 +++
 elf/dl-map-segments.h              | 16 ++++++++--
 elf/dl-minimal-malloc.c            |  3 ++
 elf/dl-open.c                      |  4 +++
 elf/dl-reloc.c                     | 50 ++++++++++++++++++++++++++++++
 elf/dl-support.c                   |  3 ++
 elf/elf.h                          |  2 ++
 elf/rtld.c                         | 12 +++++--
 elf/setup-vdso.h                   |  2 ++
 include/link.h                     |  8 +++++
 sysdeps/aarch64/dl-prop.h          |  5 +++
 sysdeps/generic/dl-mseal.h         | 23 ++++++++++++++
 sysdeps/generic/dl-prop-mseal.h    | 36 +++++++++++++++++++++
 sysdeps/generic/dl-prop.h          |  5 +++
 sysdeps/generic/ldsodefs.h         |  9 ++++++
 sysdeps/unix/sysv/linux/Makefile   |  4 +++
 sysdeps/unix/sysv/linux/dl-mseal.c | 41 ++++++++++++++++++++++++
 sysdeps/unix/sysv/linux/dl-mseal.h | 27 ++++++++++++++++
 sysdeps/x86/dl-prop.h              |  4 +++
 20 files changed, 259 insertions(+), 5 deletions(-)
 create mode 100644 sysdeps/generic/dl-mseal.h
 create mode 100644 sysdeps/generic/dl-prop-mseal.h
 create mode 100644 sysdeps/unix/sysv/linux/dl-mseal.c
 create mode 100644 sysdeps/unix/sysv/linux/dl-mseal.h
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index d65eaeadf7..723dac1ccc 100644
--- a/NEWS
+++ b/NEWS
@@ -49,6 +49,12 @@  Major new features:
   mappings to avoid further change during process execution such as protection
   permissions, unmapping, moving to another location, or shrinking the size.
 
+* The loader will memory seal all libraries that contains the new gnu
+  attribute GNU_PROPERTY_MEMORY_SEAL.  The memory sealing uses the new Linux
+  mseal syscall, and it will be applied to all shared libraries dependencies,
+  the binary, any preload and audit modules, and aby library loaded with
+  RTLD_NODELETE.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The big-endian ARC port (arceb-linux-gnu) has been removed.
diff --git a/elf/dl-load.c b/elf/dl-load.c
index e986d7faab..b52c29ccb7 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1415,6 +1415,10 @@  cannot enable executable stack as shared object requires");
     /* Assign the next available module ID.  */
     _dl_assign_tls_modid (l);
 
+  if (l->l_seal == lt_seal_toseal
+      && (mode & __RTLD_DLOPEN) && !(mode & RTLD_NODELETE))
+    l->l_seal = lt_seal_dont_dlopen;
+
 #ifdef DL_AFTER_LOAD
   DL_AFTER_LOAD (l);
 #endif
diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index 30977cf800..51c5286f6e 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -18,6 +18,7 @@ 
    <https://www.gnu.org/licenses/>.  */
 
 #include <dl-load.h>
+#include <dl-mseal.h>
 
 /* Map a segment and align it properly.  */
 
@@ -115,11 +116,15 @@  _dl_map_segments (struct link_map *l, int fd,
 	  if (__glibc_unlikely (loadcmds[nloadcmds - 1].mapstart <
 				c->mapend))
 	    return N_("ELF load command address/offset not page-aligned");
+
+	  caddr_t hole_start = (caddr_t) (l->l_addr + c->mapend);
+	  size_t hole_size = loadcmds[nloadcmds - 1].mapstart - c->mapend;
+
           if (__glibc_unlikely
-              (__mprotect ((caddr_t) (l->l_addr + c->mapend),
-                           loadcmds[nloadcmds - 1].mapstart - c->mapend,
-                           PROT_NONE) < 0))
+              (__mprotect (hole_start, hole_size, PROT_NONE) < 0))
             return DL_MAP_SEGMENTS_ERROR_MPROTECT;
+	  if (l->l_seal)
+	    _dl_mseal (hole_start, hole_size);
         }
 
       l->l_contiguous = 1;
@@ -188,6 +193,11 @@  _dl_map_segments (struct link_map *l, int fd,
                               -1, 0);
               if (__glibc_unlikely (mapat == MAP_FAILED))
                 return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL;
+	      /* We need to seal this here because it will not be part of
+		 the PT_LOAD segments, nor it is taken in RELRO
+		 calculation.  */
+	      if (l->l_seal)
+		_dl_mseal (mapat, zeroend - zeropage);
             }
         }
 
diff --git a/elf/dl-minimal-malloc.c b/elf/dl-minimal-malloc.c
index 69fc19c1b7..a701ff89d5 100644
--- a/elf/dl-minimal-malloc.c
+++ b/elf/dl-minimal-malloc.c
@@ -27,6 +27,7 @@ 
 #include <ldsodefs.h>
 #include <malloc/malloc-internal.h>
 #include <setvmaname.h>
+#include <dl-mseal.h>
 
 static void *alloc_ptr, *alloc_end, *alloc_last_block;
 
@@ -62,6 +63,8 @@  __minimal_malloc (size_t n)
       if (page == MAP_FAILED)
 	return NULL;
       __set_vma_name (page, nup, " glibc: loader malloc");
+      if (GL(dl_rtld_map).l_seal == lt_seal_toseal)
+	_dl_mseal (page, nup);
       if (page != alloc_end)
 	alloc_ptr = page;
       alloc_end = page + nup;
diff --git a/elf/dl-open.c b/elf/dl-open.c
index f283a87144..36ee904eba 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -807,6 +807,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, false);
 }
 
 void *
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 76d14830dd..f8127cb166 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
@@ -345,6 +346,7 @@  _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
     return;
   _dl_relocate_object_no_relro (l, scope, reloc_mode, consider_profiling);
   _dl_protect_relro (l);
+  _dl_mseal_map (l, false, false);
 }
 
 void
@@ -369,6 +371,54 @@  cannot apply additional memory protection after relocation");
     }
 }
 
+static void
+_dl_mseal_map_1 (struct link_map *l, bool force)
+{
+  /* The 'force' check allow to seal audit with sealing enabled after
+     they are loader during process startup.  */
+  if (l->l_seal == lt_seal_dont
+      || (force
+	  ? l->l_seal != lt_seal_dont_dlopen
+	  : l->l_seal == lt_seal_dont_dlopen))
+    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
+    {
+      /* We can use the PT_LOAD segments because even if relro splits the
+	 original RW VMA, mseal works with multiple VMAs with different
+	 flags.  */
+      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, bool force)
+{
+  if (l->l_searchlist.r_list == NULL || !dep)
+    _dl_mseal_map_1 (l, force);
+  else
+    for (unsigned int i = 0; i < l->l_searchlist.r_nlist; ++i)
+      _dl_mseal_map_1 (l->l_searchlist.r_list[i], force);
+}
+
 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 f0b6be07e9..e43b455de4 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -46,6 +46,7 @@ 
 #include <array_length.h>
 #include <dl-symbol-redir-ifunc.h>
 #include <dl-prop.h>
+#include <dl-mseal.h>
 
 extern char *__progname;
 char **_dl_argv = &__progname;	/* This is checked for some error messages.  */
@@ -100,6 +101,7 @@  static struct link_map _dl_main_map =
     .l_used = 1,
     .l_tls_offset = NO_TLS_OFFSET,
     .l_serial = 1,
+    .l_seal = lt_seal_dont,
   };
 
 /* Namespace information.  */
@@ -352,6 +354,7 @@  _dl_non_dynamic_init (void)
 
   /* Setup relro on the binary itself.  */
   _dl_protect_relro (&_dl_main_map);
+  _dl_mseal_map (&_dl_main_map, false, false);
 }
 
 #ifdef DL_SYSINFO_IMPLEMENTATION
diff --git a/elf/elf.h b/elf/elf.h
index 33aea7f743..b9fe2064af 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -1357,6 +1357,8 @@  typedef struct
 #define GNU_PROPERTY_STACK_SIZE			1
 /* No copy relocation on protected data symbol.  */
 #define GNU_PROPERTY_NO_COPY_ON_PROTECTED	2
+/* No memory sealing.  */
+#define GNU_PROPERTY_MEMORY_SEAL		3
 
 /* A 4-byte unsigned integer property: A bit is set if it is set in all
    relocatable inputs.  */
diff --git a/elf/rtld.c b/elf/rtld.c
index 21d282fc87..71902de400 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>
 
@@ -478,6 +479,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 = lt_seal_dont;
   /* Copy the TLS related data if necessary.  */
 #ifndef DONT_USE_BOOTSTRAP_MAP
 # if NO_TLS_OFFSET != 0
@@ -1042,6 +1044,11 @@  ERROR: audit interface '%s' requires version %d (maximum supported version %d);
 
   /* Mark the DSO as being used for auditing.  */
   dlmargs.map->l_auditing = 1;
+
+  /* Audit modules can not be loaded with RTLD_NODELETE, so apply the sealing
+     again on all dependencies an and ignore any possible missing seal due
+     dlopen without RTLD_NODELETE.  */
+  _dl_mseal_map (dlmargs.map, true, true);
 }
 
 /* Load all audit modules.  */
@@ -1124,6 +1131,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 = lt_seal_dont;
 
   /* A PT_LOAD segment at an unexpected address will clear the
      l_contiguous flag.  The ELF specification says that PT_LOAD
@@ -2344,8 +2352,8 @@  dl_main (const ElfW(Phdr) *phdr,
        at this point.  */
     __rtld_malloc_init_real (main_map);
 
-    if (GL(dl_rtld_map).l_relro_size != 0)
-      _dl_protect_relro (&GL(dl_rtld_map));
+    _dl_protect_relro (&GL(dl_rtld_map));
+    _dl_mseal_map (&GL(dl_rtld_map), false, false);
 
     rtld_timer_accum (&relocate_time, start);
   }
diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h
index 888e1e4897..f115e6eb78 100644
--- a/elf/setup-vdso.h
+++ b/elf/setup-vdso.h
@@ -66,6 +66,8 @@  setup_vdso (struct link_map *main_map __attribute__ ((unused)),
 
       /* The vDSO is always used.  */
       l->l_used = 1;
+      /* The PT_LOAD may not cover all the vdso mapping.  */
+      l->l_seal = lt_seal_dont;
 
       /* Initialize l_local_scope to contain just this map.  This allows
 	 the use of dl_lookup_symbol_x to resolve symbols within the vdso.
diff --git a/include/link.h b/include/link.h
index 5ed445d5a6..e8ee740099 100644
--- a/include/link.h
+++ b/include/link.h
@@ -214,6 +214,14 @@  struct link_map
 					       lt_library map.  */
     unsigned int l_tls_in_slotinfo:1; /* TLS slotinfo updated in dlopen.  */
 
+    enum			/* Memory sealing status.  */
+      {
+	lt_seal_dont = 0,	/* Do not seal the object.  */
+	lt_seal_dont_dlopen,    /* Do not seal from a dlopen.  */
+	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,
        potentially from signal handlers.  Initial loading of an
diff --git a/sysdeps/aarch64/dl-prop.h b/sysdeps/aarch64/dl-prop.h
index df05c0211d..c66d9a49f0 100644
--- a/sysdeps/aarch64/dl-prop.h
+++ b/sysdeps/aarch64/dl-prop.h
@@ -19,6 +19,8 @@ 
 #ifndef _DL_PROP_H
 #define _DL_PROP_H
 
+#include <dl-prop-mseal.h>
+
 extern void _dl_bti_protect (struct link_map *, int) attribute_hidden;
 
 extern void _dl_bti_check (struct link_map *, const char *)
@@ -45,6 +47,9 @@  static inline int
 _dl_process_gnu_property (struct link_map *l, int fd, uint32_t type,
 			  uint32_t datasz, void *data)
 {
+  if (_dl_process_gnu_property_seal (l, fd, type, datasz, data))
+    return 0;
+
   if (!GLRO(dl_aarch64_cpu_features).bti)
     /* Skip note processing.  */
     return 0;
diff --git a/sysdeps/generic/dl-mseal.h b/sysdeps/generic/dl-mseal.h
new file mode 100644
index 0000000000..dccf78ae38
--- /dev/null
+++ b/sysdeps/generic/dl-mseal.h
@@ -0,0 +1,23 @@ 
+/* 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;
+}
diff --git a/sysdeps/generic/dl-prop-mseal.h b/sysdeps/generic/dl-prop-mseal.h
new file mode 100644
index 0000000000..b1f93a17fb
--- /dev/null
+++ b/sysdeps/generic/dl-prop-mseal.h
@@ -0,0 +1,36 @@ 
+/* Support for GNU properties.  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/>.  */
+
+#ifndef _DL_PROP_MSEAL_H
+#define _LD_PROP_MSEAL_H
+
+#include <dl-tunables.h>
+
+static __always_inline bool
+_dl_process_gnu_property_seal (struct link_map *l, int fd, uint32_t type,
+			       uint32_t datasz, void *data)
+{
+  if (type == GNU_PROPERTY_MEMORY_SEAL && datasz == 0)
+    {
+      l->l_seal = lt_seal_toseal;
+      return true;
+    }
+  return false;
+}
+
+#endif
diff --git a/sysdeps/generic/dl-prop.h b/sysdeps/generic/dl-prop.h
index 1d92920a96..5fac690c81 100644
--- a/sysdeps/generic/dl-prop.h
+++ b/sysdeps/generic/dl-prop.h
@@ -19,6 +19,8 @@ 
 #ifndef _DL_PROP_H
 #define _DL_PROP_H
 
+#include <dl-prop-mseal.h>
+
 /* The following functions are used by the dynamic loader and the
    dlopen machinery to process PT_NOTE and PT_GNU_PROPERTY entries in
    the binary or shared object.  The notes can be used to change the
@@ -47,6 +49,9 @@  static inline int __attribute__ ((always_inline))
 _dl_process_gnu_property (struct link_map *l, int fd, uint32_t type,
 			  uint32_t datasz, void *data)
 {
+  if (_dl_process_gnu_property_seal (l, fd, type, datasz, data))
+    return 0;
+
   /* Continue until GNU_PROPERTY_1_NEEDED is found.  */
   if (type == GNU_PROPERTY_1_NEEDED)
     {
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 91447a5e77..dbfa5d7a6a 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1024,6 +1024,15 @@  void _dl_relocate_object_no_relro (struct link_map *map,
 /* Protect PT_GNU_RELRO area.  */
 extern void _dl_protect_relro (struct link_map *map) attribute_hidden;
 
+/* Issue memory sealing for the link map MAP.  If MAP is contiguous the
+   whole region is sealed, otherwise iterate over the program headerrs and
+   seal each PT_LOAD segment.i
+   The DEP specify whether to seal the dependencies as well, while FORCE
+   ignores any possible missing seal due dlopen without RTLD_NODELETE.
+   The memory sealing should be done *after* RELRO setup.  */
+extern void _dl_mseal_map (struct link_map *map, bool dep, bool force)
+     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 a270b0af4c..8c1389cb0f 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -644,6 +644,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..c99fd991cb
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.c
@@ -0,0 +1,41 @@ 
+/* 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-tunables.h>
+#include <ldsodefs.h>
+
+int
+_dl_mseal (void *addr, size_t len)
+{
+  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
+  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..25e3f724dc
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.h
@@ -0,0 +1,27 @@ 
+/* 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;
diff --git a/sysdeps/x86/dl-prop.h b/sysdeps/x86/dl-prop.h
index 08387dfaff..26a687d611 100644
--- a/sysdeps/x86/dl-prop.h
+++ b/sysdeps/x86/dl-prop.h
@@ -19,6 +19,7 @@ 
 #ifndef _DL_PROP_H
 #define _DL_PROP_H
 
+#include <dl-prop-mseal.h>
 #include <libintl.h>
 
 extern void _dl_cet_check (struct link_map *, const char *)
@@ -241,6 +242,9 @@  _dl_process_gnu_property (struct link_map *l, int fd, uint32_t type,
 			  uint32_t datasz, void *data)
 {
   /* This is called on each GNU property.  */
+  if (_dl_process_gnu_property_seal (l, fd, type, datasz, data))
+    return 0;
+
   unsigned int needed_1 = 0;
   unsigned int feature_1_and = 0;
   unsigned int isa_1_needed = 0;