@@ -35,6 +35,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.
@@ -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
@@ -18,6 +18,7 @@
<https://www.gnu.org/licenses/>. */
#include <dl-load.h>
+#include <dl-mseal.h>
/* Map a segment and align it properly. */
@@ -188,6 +189,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);
}
}
@@ -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;
@@ -793,6 +793,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 *
@@ -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, false);
}
@@ -369,6 +375,51 @@ 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
+ {
+ 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)
@@ -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. */
@@ -353,6 +355,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, false);
}
#ifdef DL_SYSINFO_IMPLEMENTATION
@@ -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. */
@@ -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
@@ -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.
@@ -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
@@ -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;
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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
@@ -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)
{
@@ -1017,6 +1017,14 @@ 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;
+/* 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. */
+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. */
@@ -643,6 +643,10 @@ sysdep-rtld-routines += \
dl-sbrk \
# sysdep-rtld-routines
+dl-routines += \
+ dl-mseal \
+ # dl-routines
+
others += \
pldd \
# others
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
@@ -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;