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.

  * Any preload libraries.

  * Any library loaded with dlopen with RTLD_NODELETE flag (including
    libgcc.so loaded to enable unwind and/or thread cancellation).

  * Audit modules.

  * The loader bump allocator.

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 memory sealing is done.

  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.
This commit is contained in:
Adhemerval Zanella 2024-06-02 20:31:27 -03:00
parent 39ed316b6b
commit 6a785f1dcf
31 changed files with 865 additions and 0 deletions

11
NEWS
View File

@ -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]

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;

29
elf/dl-mseal-mode.h Normal file
View File

@ -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

View File

@ -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 *

View File

@ -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)

View File

@ -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

View File

@ -135,6 +135,12 @@ glibc {
maxval: 1
default: 0
}
seal {
type: INT_32
minval: 0
maxval: 2
default: 1
}
}
mem {

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -17,6 +17,7 @@
<https://www.gnu.org/licenses/>. */
#include <stdio.h>
#include <string.h>
const char *
strerrorname_np (int errnum)

View File

@ -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

View File

@ -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. */

View File

@ -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)

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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; }

View File

@ -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; }

View File

@ -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; }

View File

@ -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; }

View File

@ -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; }

View File

@ -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; }

View File

@ -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; }

View File

@ -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>

View File

@ -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"

View File

@ -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"