@@ -59,6 +59,12 @@ Major new features:
memory sealing by default if the toochain supports it. A new configure
option, --disable-default-memory-seal, disables it.
+* A new tunable, glibc.rtld.seal, can enable memory sealing on the program
+ and all its dependencies. The tunable accepts two different values,
+ with '0' applying the GNU attribute GNU_PROPERTY_MEMORY_SEAL (if present),
+ or '1' to enforce sealing the program and its dependencies (including
+ preload, audit modules, and objects opened with RTLD_NODELETE).
+
Deprecated and removed features, and other changes affecting compatibility:
* The big-endian ARC port (arceb-linux-gnu) has been removed.
@@ -1315,6 +1315,9 @@ cannot enable executable stack as shared object requires");
break;
}
+ /* Update the sealing mode based on the tunable. */
+ _dl_mseal_update_map (l, mode);
+
/* We are done mapping in the file. We no longer need the descriptor. */
if (__glibc_unlikely (__close_nocancel (fd) != 0))
{
new file mode 100644
@@ -0,0 +1,28 @@
+/* Memory sealing tunable. 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_MSEAL_MODE_H
+#define _DL_MSEAL_MODE_H
+
+enum dl_seal_mode
+{
+ DL_SEAL_DEFAULT = 0,
+ DL_SEAL_ENFORCE = 1,
+};
+
+#endif
@@ -29,6 +29,8 @@
#include <libc-pointer-arith.h>
#include "dynamic-link.h"
#include <dl-mseal.h>
+#include <dl-mseal-mode.h>
+#include <dl-tunables.h>
/* Statistics function. */
#ifdef SHARED
@@ -371,6 +373,17 @@ cannot apply additional memory protection after relocation");
}
}
+void
+_dl_mseal_update_map (struct link_map *map, int mode)
+{
+ /* Also enable forced sealing on audit modules, loader will apply it
+ after the modules is being loaded and validated. */
+ if (TUNABLE_GET (glibc, rtld, seal, int32_t, NULL) == DL_SEAL_ENFORCE
+ && (!(mode & __RTLD_DLOPEN)
+ || (mode & RTLD_NODELETE) || (mode & __RTLD_AUDIT)))
+ map->l_seal = lt_seal_toseal;
+}
+
static void
_dl_mseal_map_1 (struct link_map *l, bool force)
{
@@ -349,6 +349,8 @@ _dl_non_dynamic_init (void)
_dl_process_pt_gnu_property (&_dl_main_map, -1, &ph[-1]);
break;
}
+ /* Update the sealing mode based on the tunable. */
+ _dl_mseal_update_map (&_dl_main_map, 0);
call_function_static_weak (_dl_find_object_init);
@@ -135,6 +135,12 @@ glibc {
maxval: 1
default: 0
}
+ seal {
+ type: INT_32
+ minval: 0
+ maxval: 1
+ default: 0
+ }
}
mem {
@@ -1247,6 +1247,9 @@ rtld_setup_main_map (struct link_map *main_map)
break;
}
+ /* Update the sealing mode based on the tunable. */
+ _dl_mseal_update_map (main_map, 0);
+
/* Adjust the address of the TLS initialization image in case
the executable is actually an ET_DYN object. */
if (main_map->l_tls_initimage != NULL)
@@ -1766,6 +1769,8 @@ dl_main (const ElfW(Phdr) *phdr,
break;
}
+ _dl_mseal_update_map (&GL(dl_rtld_map), 0);
+
/* Add the dynamic linker to the TLS list if it also uses TLS. */
if (GL(dl_rtld_map).l_tls_blocksize != 0)
/* Assign a module ID. Do this before loading any audit modules. */
@@ -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: 1)
@@ -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 mapped memory region, such as shrinking
+or expanding, mapping another segment over a pre-existing region, or changing
+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
+immutable 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 ones. In both cases, it is
+up either to binary or the loader to set up the sealing.
+
+@item
+Any preload libraries.
+
+@item
+Any library loaded with @code{dlopen} with @code{RTLD_NODELETE} flag.
+
+@item
+All audit modules and their dependencies.
+@end itemize
+
+The tunable accepts two values: @samp{0} where sealing applies the GNU attribute
+@code{GNU_PROPERTY_MEMORY_SEAL} if present, and @samp{1} where sealing is
+enforced on the binary and its dependencies. For the enforced mode,
+if the memory can not be sealed the process terminates the execution.
+
+The default value of this tunable is @samp{0}.
+@end deftp
+
@node Elision Tunables
@section Elision Tunables
@cindex elision tunables
@@ -1024,6 +1024,12 @@ 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;
+/* The the sealing mode of MAP based on open MODE and on the rtld.seal
+ tunable. */
+extern void _dl_mseal_update_map (struct link_map *map,
+ int mode)
+ 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
@@ -668,6 +668,7 @@ tests += \
$(tests-static) \
tst-dl_mseal \
tst-dl_mseal-noseal \
+ tst-dl_mseal-tunable \
# tests
modules-names += \
@@ -707,6 +708,16 @@ $(objpfx)tst-dl_mseal-noseal.out: \
$(objpfx)tst-dl_mseal-dlopen-2-noseal.so \
$(objpfx)tst-dl_mseal-dlopen-2-1-noseal.so
+$(objpfx)tst-dl_mseal-tunable.out: \
+ $(objpfx)tst-dl_mseal-auditmod-noseal.so \
+ $(objpfx)tst-dl_mseal-preload-noseal.so \
+ $(objpfx)tst-dl_mseal-mod-1-noseal.so \
+ $(objpfx)tst-dl_mseal-mod-2-noseal.so \
+ $(objpfx)tst-dl_mseal-dlopen-1.so \
+ $(objpfx)tst-dl_mseal-dlopen-1-1.so \
+ $(objpfx)tst-dl_mseal-dlopen-2-noseal.so \
+ $(objpfx)tst-dl_mseal-dlopen-2-1-noseal.so
+
LDFLAGS-tst-dl_mseal = -Wl,--no-as-needed
LDFLAGS-tst-dl_mseal-mod-1.so = -Wl,--no-as-needed
LDFLAGS-tst-dl_mseal-dlopen-1.so = -Wl,--no-as-needed
@@ -739,10 +750,14 @@ $(objpfx)tst-dl_mseal-dlopen-2-noseal.so: $(objpfx)tst-dl_mseal-dlopen-2-1-nosea
tst-dl_mseal-static-noseal-no-memory-seal = yes
+tst-dl_mseal-tunable-no-memory-seal = yes
+$(objpfx)tst-dl_mseal-tunable: $(objpfx)tst-dl_mseal-mod-1-noseal.so
+
tst-dl_mseal-ARGS = -- $(host-test-program-cmd)
tst-dl_mseal-static-ARGS = -- $(host-test-program-cmd)
tst-dl_mseal-noseal-ARGS = -- $(host-test-program-cmd)
tst-dl_mseal-static-noseal-ARGS = -- $(host-test-program-cmd)
+tst-dl_mseal-tunable-ARGS = -- $(host-test-program-cmd)
endif
endif
@@ -17,6 +17,7 @@
<https://www.gnu.org/licenses/>. */
#include <atomic.h>
+#include <dl-mseal-mode.h>
#include <dl-mseal.h>
#include <dl-tunables.h>
#include <ldsodefs.h>
@@ -37,5 +38,11 @@ _dl_mseal (void *addr, size_t len)
atomic_store_relaxed (&mseal_supported, false);
}
#endif
+ if (TUNABLE_GET (glibc, rtld, seal, int32_t, NULL) == 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;
}
@@ -248,6 +248,9 @@ do_test (int argc, char *argv[])
#ifndef TEST_STATIC
(char *) "LD_PRELOAD=" LIB_PRELOAD,
(char *) "LD_AUDIT=" LIB_AUDIT,
+#endif
+#ifdef TUNABLE_ENV_VAR
+ (char *) "GLIBC_TUNABLES=" TUNABLE_ENV_VAR,
#endif
NULL
};
new file mode 100644
@@ -0,0 +1,76 @@
+/* Basic tests for sealing. Check the tunable in enforce mode.
+ 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>
+
+/* This test checks the glibc.rtld.seal enforces sealing on multiple
+ places:
+
+ - On the binary itself.
+ - On a LD_PRELOAD library.
+ - On a depedency module (tst-dl_mseal-mod-2-noseal.so).
+ - On a audit modules (tst-dl_mseal-auditmod-noeal.so).
+ - On a dlopen dependency opened with RTLD_NODELET
+ (tst-dl_mseal-dlopen-2-noseal.so).
+*/
+
+#define TUNABLE_ENV_VAR "glibc.rtld.seal=1"
+
+#define LIB_PRELOAD "tst-dl_mseal-preload-noseal.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-noseal.so"
+#define LIB_DLOPEN_NODELETE_DEP "tst-dl_mseal-dlopen-2-1-noseal.so"
+
+#define LIB_AUDIT "tst-dl_mseal-auditmod-noseal.so"
+
+/* Expected libraries that loader will seal. */
+static const char *expected_sealed_vmas[] =
+{
+ "tst-dl_mseal-tunable",
+ "libc.so",
+ "ld.so",
+ "tst-dl_mseal-mod-1-noseal.so",
+ "tst-dl_mseal-mod-2-noseal.so",
+ LIB_DLOPEN_NODELETE,
+ LIB_DLOPEN_NODELETE_DEP,
+ LIB_AUDIT,
+ LIB_PRELOAD,
+};
+
+/* Expected non sealed libraries. */
+static const char *expected_non_sealed_vmas[] =
+{
+ LIB_DLOPEN_DEFAULT,
+ LIB_DLOPEN_DEFAULT_DEP,
+ /* Auxiary pages mapped by the kernel. */
+ "[vdso]",
+ "[sigpage]",
+};
+
+/* Special pages, either Auxiliary kernel pages where permission can not be
+ changed or auxiliary libs that we can know prior hand that sealing is
+ enabled. */
+static const char *expected_non_sealed_special[] =
+{
+ LIBGCC_S_SO,
+ "[vectors]",
+};
+
+#include "tst-dl_mseal-skeleton.c"