@@ -29,6 +29,17 @@ Major new features:
mappings to avoid further change during process execution such as protection
permissions, unmapping, moving to another location, or shrinking the size.
+* On Linux, the loader will memory seal multiple places where the memory is
+ supposed to immutable over program execution: all shared library
+ dependencies from the binary, the binary itself, any preload libraries,
+ any library loaded with dlopen and the RTLD_NODELETE flag, any audit modules,
+ and the loader metadata.
+
+* A new tunable, glibc.rtld.seal, can be used to control the memory sealing
+ with thread different states: disable, where sealing will not be applied,
+ enabled, where any memory sealing failure is ignored; and enforced, where
+ any memory failure terminates the process. The default is enabled.
+
Deprecated and removed features, and other changes affecting compatibility:
[Add deprecations, removals and changes affecting compatibility here]
@@ -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
@@ -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,10 @@ _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. */
+ _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,7 @@ __minimal_malloc (size_t n)
if (page == MAP_FAILED)
return NULL;
__set_vma_name (page, nup, " glibc: loader malloc");
+ _dl_mseal (page, nup);
if (page != alloc_end)
alloc_ptr = page;
alloc_end = page + nup;
new file mode 100644
@@ -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
@@ -838,6 +838,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 *
@@ -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,51 @@ cannot apply additional memory protection after relocation");
}
}
+static void
+_dl_mseal_map_1 (struct link_map *l)
+{
+ /* We only checked if the map is already sealed here so we can seal audit
+ module dependencies after the initial audit setup. */
+ 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_seal == lt_seal_sealed)
+ 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)
@@ -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
@@ -135,6 +135,12 @@ glibc {
maxval: 1
default: 0
}
+ seal {
+ type: INT_32
+ minval: 0
+ maxval: 2
+ default: 1
+ }
}
mem {
@@ -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
@@ -1043,6 +1045,10 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
/* Mark the DSO as being used for auditing. */
dlmargs.map->l_auditing = 1;
+
+ /* Seal the audit modules and their dependencies. */
+ dlmargs.map->l_seal = lt_seal_toseal;
+ _dl_mseal_map (dlmargs.map, true);
}
/* Load all audit modules. */
@@ -1125,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 = 1;
/* 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.
@@ -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)
@@ -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,
@@ -355,6 +355,48 @@ 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.
+
+@item
+Any runtime library used for process unwind (such as required by @code{backtrace}
+or @code{pthread_exit}).
+
+@item
+All audit modules and their dependencies.
+@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
@@ -17,6 +17,7 @@
<https://www.gnu.org/licenses/>. */
#include <stdio.h>
+#include <string.h>
const char *
strerrorname_np (int errnum)
new file mode 100644
@@ -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
@@ -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. */
@@ -629,6 +629,10 @@ sysdep-rtld-routines += \
dl-sbrk \
# sysdep-rtld-routines
+dl-routines += \
+ dl-mseal \
+ # dl-routines
+
others += \
pldd \
# others
@@ -638,6 +642,49 @@ 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 += \
+ tst-dl_mseal-auditmod \
+ tst-dl_mseal-dlopen-1 \
+ tst-dl_mseal-dlopen-1-1 \
+ tst-dl_mseal-dlopen-2 \
+ tst-dl_mseal-dlopen-2-1 \
+ tst-dl_mseal-mod-1 \
+ tst-dl_mseal-mod-2 \
+ tst-dl_mseal-preload \
+ # modules-names
+
+$(objpfx)tst-dl_mseal.out: \
+ $(objpfx)tst-dl_mseal-auditmod.so \
+ $(objpfx)tst-dl_mseal-preload.so \
+ $(objpfx)tst-dl_mseal-mod-1.so \
+ $(objpfx)tst-dl_mseal-mod-2.so \
+ $(objpfx)tst-dl_mseal-dlopen-1.so \
+ $(objpfx)tst-dl_mseal-dlopen-1-1.so \
+ $(objpfx)tst-dl_mseal-dlopen-2.so \
+ $(objpfx)tst-dl_mseal-dlopen-2-1.so
+
+tst-dl_mseal-ARGS = -- $(host-test-program-cmd)
+$(objpfx)tst-dl_mseal: $(objpfx)tst-dl_mseal-mod-1.so
+LDFLAGS-tst-dl_mseal = -Wl,--no-as-needed
+$(objpfx)tst-dl_mseal-mod-1.so: $(objpfx)tst-dl_mseal-mod-2.so
+LDFLAGS-tst-dl_mseal-mod-1.so = -Wl,--no-as-needed
+
+$(objpfx)tst-dl_mseal-dlopen-1.so: $(objpfx)tst-dl_mseal-dlopen-1-1.so
+LDFLAGS-tst-dl_mseal-dlopen-1.so = -Wl,--no-as-needed
+$(objpfx)tst-dl_mseal-dlopen-2.so: $(objpfx)tst-dl_mseal-dlopen-2-1.so
+LDFLAGS-tst-dl_mseal-dlopen-2.so = -Wl,--no-as-needed
+
+tst-dl_mseal-static-ARGS = -- $(host-test-program-cmd)
endif
ifeq ($(subdir),rt)
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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
new file mode 100644
@@ -0,0 +1,23 @@
+/* Audit 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/>. */
+
+unsigned int
+la_version (unsigned int v)
+{
+ return v;
+}
new file mode 100644
@@ -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; }
new file mode 100644
@@ -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; }
new file mode 100644
@@ -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; }
new file mode 100644
@@ -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; }
new file mode 100644
@@ -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; }
new file mode 100644
@@ -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; }
new file mode 100644
@@ -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 foo (void) { return 42; }
new file mode 100644
@@ -0,0 +1,272 @@
+/* Basic tests for sealing. Static 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 <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>
+#include <support/xthread.h>
+
+#if UINTPTR_MAX == UINT64_MAX
+# define PTR_FMT "#018" PRIxPTR
+#else
+# define PTR_FMT "#010" PRIxPTR
+#endif
+
+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);
+}
+
+/* Libraries/VMA that could not be sealed, and that checking for sealing
+ does not work (kernel does not allow changing protection). */
+static const char *non_sealed_vmas[] =
+{
+ ".", /* basename value for empty string anonymous
+ mappings. */
+ "[heap]",
+ "[vsyscall]",
+ "[vvar]",
+ "[stack]",
+ "zero", /* /dev/zero */
+};
+
+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;
+}
+#define IS_IN_STRING_LIST(__s, __list) \
+ is_in_string_list (__s, __list, array_length (__list))
+
+static void *
+tf (void *closure)
+{
+ pthread_exit (NULL);
+ return NULL;
+}
+
+static int
+handle_restart (void)
+{
+#ifndef TEST_STATIC
+ xdlopen (LIB_DLOPEN_NODELETE, RTLD_NOW | RTLD_NODELETE);
+ xdlopen (LIB_DLOPEN_DEFAULT, RTLD_NOW);
+#endif
+
+ /* pthread_exit will load LIBGCC_S_SO. */
+ xpthread_join (xpthread_create (NULL, tf, NULL));
+
+ 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))
+ != -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: %" PTR_FMT "-%" PTR_FMT " %s %s\n",
+ start,
+ end,
+ flags,
+ name);
+
+ found_expected[idx] = true;
+ found = true;
+ }
+ else if ((idx = IS_IN_STRING_LIST (libname, expected_non_sealed_libs))
+ != -1)
+ {
+ /* Check if expected non-sealed segments protection can indeed be
+ changed. The idea is to use something that would not break
+ process execution, so just try to mprotect with all protection
+ bits. */
+ int new_prot = PROT_READ | PROT_WRITE | PROT_EXEC;
+ TEST_VERIFY_EXIT (mprotect ((void *) start, end - start, new_prot)
+ == 0);
+
+ printf ("not-sealed: vma: %" PTR_FMT "-%" PTR_FMT " %s %s\n",
+ start,
+ end,
+ flags,
+ name);
+
+ found = true;
+ }
+
+ if (!found)
+ {
+ if (IS_IN_STRING_LIST (libname, non_sealed_vmas) != -1)
+ printf ("not-sealed: vma: %" PTR_FMT "-%" PTR_FMT " %s %s\n",
+ start,
+ end,
+ flags,
+ name);
+ else
+ FAIL_EXIT1 ("unexpected vma: %" PTR_FMT "-%" PTR_FMT " %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[4];
+ envvarss[0] = (char *) "GLIBC_TUNABLES=glibc.rtld.seal=2";
+#ifndef TEST_STATIC
+ envvarss[1] = (char *) "LD_PRELOAD=" LIB_PRELOAD;
+ envvarss[2] = (char *) "LD_AUDIT=" LIB_AUDIT,
+ envvarss[3] = 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>
new file mode 100644
@@ -0,0 +1,36 @@
+/* Basic tests for sealing. Static 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/>. */
+
+/* This test checks the memory sealing work on a statically built binary. */
+
+#define GLIBC_RTLD_SEAL "2"
+#define TEST_STATIC 1
+
+/* Expected libraries that loader will seal. */
+static const char *expected_sealed_libs[] =
+{
+ "tst-dl_mseal-static",
+};
+
+/* Expected non sealed libraries. */
+static const char *expected_non_sealed_libs[] =
+{
+ "[vdso]",
+};
+
+#include "tst-dl_mseal-skeleton.c"
new file mode 100644
@@ -0,0 +1,67 @@
+/* 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 <gnu/lib-names.h>
+
+/* Check if memory sealing works as expected on multiples places:
+ - On the binary itself.
+ - On a LD_PRELOAD library.
+ - On a depedency modules (tst-dl_mseal-mod-{1,2}.so).
+ - On a audit modules (tst-dl_mseal-auditmod.so).
+ - On a dlopen dependency opened with RTLD_NODELET
+ (tst-dl_mseal-dlopen-{2,2-1}.so).
+ - On the libgcc_s opened by thread unwind.
+*/
+
+#define GLIBC_RTLD_SEAL "2"
+#define LIB_AUDIT "tst-dl_mseal-auditmod.so"
+
+#define LIB_PRELOAD "tst-dl_mseal-preload.so"
+#define LIB_AUDIT "tst-dl_mseal-auditmod.so"
+
+#define LIB_DLOPEN_DEFAULT "tst-dl_mseal-dlopen-1.so"
+#define LIB_DLOPEN_DEFAULT_DEP "tst-dl_mseal-dlopen-1-1.so"
+#define LIB_DLOPEN_NODELETE "tst-dl_mseal-dlopen-2.so"
+#define LIB_DLOPEN_NODELETE_DEP "tst-dl_mseal-dlopen-2-1.so"
+
+/* Expected libraries that loader will seal. */
+static const char *expected_sealed_libs[] =
+{
+ "libc.so",
+ "ld.so",
+ "tst-dl_mseal",
+ "tst-dl_mseal-mod-1.so",
+ "tst-dl_mseal-mod-2.so",
+ LIB_PRELOAD,
+ LIB_AUDIT,
+ LIB_DLOPEN_NODELETE,
+ LIB_DLOPEN_NODELETE_DEP,
+ LIBGCC_S_SO,
+};
+
+/* Expected non sealed libraries. */
+static const char *expected_non_sealed_libs[] =
+{
+ "tst-dl_mseal-no-memory-seal",
+ LIB_PRELOAD,
+ LIB_DLOPEN_DEFAULT,
+ LIB_DLOPEN_DEFAULT_DEP,
+ "[vdso]",
+};
+
+#include "tst-dl_mseal-skeleton.c"