mseal sysmap: Choose system mapping sealing at boot
Subsystems (18)
v2 cover mseal sysmap: Choose system mapping sealing at boot
System mapping sealing is currently a build time choice. Hosts which want it and hosts which need to move or unmap these mappings therefore need separate kernels, which is not ideal. Let's allow the administrator to choose at boot time.
v2:
- Skip destructive vDSO tests on unsealed 32-bit x86 mappings
- Move the sysmap tests into selftests/mm (Lorenzo)
- Use shared vm_util helpers in the sysmap tests (Lorenzo)
- Use PROCMAP_QUERY for the vDSO lookup (Lorenzo)
- Keep test changes in separate patches (Lorenzo)
- Put the enforcement tests before the kernel changes (Lorenzo)
- Move MSEAL_SYSTEM_MAPPINGS to mm/Kconfig (Lorenzo)
- Remove VM_SEALED_SYSMAP (Lorenzo)
- Seal mappings in __install_special_mapping() (Lorenzo)
- Leave a sealed uprobe trampoline mapped if optimisation fails
- Replace the boolean parameter with no_mseal_sys_mappings (Lorenzo)
- Drop MSEAL_SYSTEM_MAPPINGS_DEFAULT_ON (Lorenzo)
- Drop the CHECKPOINT_RESTORE dependency (Lorenzo)
- Document the software which sealing breaks (Lorenzo)
- Keep Kconfig default-n regardless of CHECKPOINT_RESTORE (Andrei)
- Retain sealing by default when built in, with no_mseal_sys_mappings as the boot-time opt-out (Lorenzo)
- Explain the security rationale (Lorenzo)
- Describe the Meta/Reverie use case (Lorenzo)
- Skip general checks on unsealed kernels (Lorenzo)
- Add expect_sealed for strict CI checks (Jeff)
- Use the existing -T filter to exclude strict checks when needed (Jeff)
- Test that no_mseal_sys_mappings leaves mappings unsealed (Jeff)
- Add the vDSO mprotect() enforcement test (Jeff)
v1: https://lore.kernel.org/r/[email protected]
Chris Down (8):
selftests/mm: Move sysmap_is_sealed into mm
selftests/mm: Skip sysmap_is_sealed when system mappings aren't sealed selftests/mm: Test that the seal on system mappings is enforced mm/mseal: Move MSEAL_SYSTEM_MAPPINGS to mm/Kconfig uprobes/x86: Leave sealed trampolines in place on optimisation failure mm/mseal: Seal system mappings in __install_special_mapping() mm/mseal: Add no_mseal_sys_mappings boot parameter selftests/mm: Check that no_mseal_sys_mappings unseals system mappings
.../admin-guide/kernel-parameters.txt | 7 + Documentation/userspace-api/mseal.rst | 12 +- arch/arm64/kernel/vdso.c | 9 +- arch/loongarch/kernel/vdso.c | 3 +- arch/riscv/kernel/vdso.c | 2 +- arch/s390/kernel/vdso.c | 2 +- arch/x86/entry/vdso/vma.c | 5 +- arch/x86/kernel/uprobes.c | 2 +- include/linux/mm.h | 6 - init/Kconfig | 5 +- kernel/events/uprobes.c | 2 +- lib/vdso/datastore.c | 2 +- mm/Kconfig | 23 ++ mm/mseal.c | 12 + mm/vma.c | 2 + mm/vma.h | 8 + security/Kconfig | 21 -- tools/testing/selftests/Makefile | 1 - tools/testing/selftests/mm/Makefile | 1 + tools/testing/selftests/mm/config | 1 + tools/testing/selftests/mm/run_vmtests.sh | 6 + tools/testing/selftests/mm/sysmap_is_sealed.c | 259 ++++++++++++++++++ tools/testing/selftests/mm/vm_util.c | 5 + tools/testing/selftests/mm/vm_util.h | 1 + .../mseal_system_mappings/.gitignore | 2 - .../selftests/mseal_system_mappings/Makefile | 6 - .../selftests/mseal_system_mappings/config | 1 - .../mseal_system_mappings/sysmap_is_sealed.c | 119 -------- 28 files changed, 345 insertions(+), 180 deletions(-) create mode 100644 tools/testing/selftests/mm/sysmap_is_sealed.c delete mode 100644 tools/testing/selftests/mseal_system_mappings/.gitignore delete mode 100644 tools/testing/selftests/mseal_system_mappings/Makefile delete mode 100644 tools/testing/selftests/mseal_system_mappings/config delete mode 100644 tools/testing/selftests/mseal_system_mappings/sysmap_is_sealed.c
base-commit: 734a9da61a5bc5d82b496a86bacc097d11dc6871
Signature · 1 line
2.54.0
PATCH v2 1/8 selftests/mm: Move sysmap_is_sealed into mm
System mapping sealing is an mm feature, but its test has its own directory under tools/testing/selftests rather than living with the other mm tests.
Let's move it into tools/testing/selftests/mm and run it with the mmap tests. Its config now comes from mm's config fragment. The test itself is unchanged.
---
tools/testing/selftests/Makefile | 1 -
tools/testing/selftests/mm/Makefile | 1 +
tools/testing/selftests/mm/config | 1 +
tools/testing/selftests/mm/run_vmtests.sh | 1 +
.../{mseal_system_mappings => mm}/sysmap_is_sealed.c | 0
tools/testing/selftests/mseal_system_mappings/.gitignore | 2 --
tools/testing/selftests/mseal_system_mappings/Makefile | 6 ------
tools/testing/selftests/mseal_system_mappings/config | 1 -
8 files changed, 3 insertions(+), 10 deletions(-)
rename tools/testing/selftests/{mseal_system_mappings => mm}/sysmap_is_sealed.c (100%)
delete mode 100644 tools/testing/selftests/mseal_system_mappings/.gitignore
delete mode 100644 tools/testing/selftests/mseal_system_mappings/Makefile
delete mode 100644 tools/testing/selftests/mseal_system_mappings/configtools/testing/selftests/Makefile +0 -1
@@ -84,7 +84,6 @@ TARGETS += mount TARGETS += mount_setattr TARGETS += move_mount_set_group TARGETS += mqueue -TARGETS += mseal_system_mappings TARGETS += nci TARGETS += net TARGETS += net/af_unix
tools/testing/selftests/mm/Makefile +1 -0
@@ -110,6 +110,7 @@ TEST_GEN_FILES += folio_order_check TEST_GEN_FILES += khugepaged_sync_check TEST_GEN_FILES += khugepaged_race TEST_GEN_FILES += soft-dirty +TEST_GEN_FILES += sysmap_is_sealed ifeq ($(ARCH),x86_64) CAN_BUILD_I386 := $(shell ./../x86/check_cc.sh "$(CC)" ../x86/trivial_32bit_program.c -m32)
tools/testing/selftests/mm/config +1 -0
@@ -14,3 +14,4 @@ CONFIG_UPROBES=y CONFIG_MEMORY_FAILURE=y CONFIG_HWPOISON_INJECT=m CONFIG_PROC_MEM_ALWAYS_FORCE=y +CONFIG_MSEAL_SYSTEM_MAPPINGS=y
tools/testing/selftests/mm/run_vmtests.sh +1 -0
@@ -307,6 +307,7 @@ fi CATEGORY="mmap" run_test ./map_populate CATEGORY="mmap" run_test ./droppable +CATEGORY="mmap" run_test ./sysmap_is_sealed CATEGORY="mlock" run_test ./mlock-random-test
tools/testing/selftests/mm/sysmap_is_sealed.c renamed from tools/testing/selftests/mseal_system_mappings/sysmap_is_sealed.c +0 -0
tools/testing/selftests/mseal_system_mappings/.gitignore deleted +0 -2
@@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -sysmap_is_sealed
tools/testing/selftests/mseal_system_mappings/Makefile deleted +0 -6
@@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -CFLAGS += -std=c99 -pthread -Wall $(KHDR_INCLUDES) - -TEST_GEN_PROGS := sysmap_is_sealed - -include ../lib.mk
tools/testing/selftests/mseal_system_mappings/config deleted +0 -1
@@ -1 +0,0 @@ -CONFIG_MSEAL_SYSTEM_MAPPINGS=y
Signature · 1 line
2.54.0
PATCH v2 2/8 selftests/mm: Skip sysmap_is_sealed when system mappings aren't sealed
In reply to Chris DownNow that sysmap_is_sealed runs with the other mm tests, it also runs on kernels built without CONFIG_MSEAL_SYSTEM_MAPPINGS, where it fails. Userspace can't tell whether the option is enabled, but a sealed [vdso] in a new process means that it is.
So let's use [vdso] as the reference: skip if it isn't sealed, and otherwise expect the other system mappings to be sealed too, and [stack] not to be.
--- tools/testing/selftests/mm/run_vmtests.sh | 7 +++++- tools/testing/selftests/mm/sysmap_is_sealed.c | 22 +++++++++++++++++++ tools/testing/selftests/mm/vm_util.c | 5 +++++ tools/testing/selftests/mm/vm_util.h | 1 + 4 files changed, 34 insertions(+), 1 deletion(-)
tools/testing/selftests/mm/run_vmtests.sh +6 -1
@@ -307,7 +307,12 @@ fi CATEGORY="mmap" run_test ./map_populate CATEGORY="mmap" run_test ./droppable -CATEGORY="mmap" run_test ./sysmap_is_sealed +if gzip -dcfq /proc/config.gz "/boot/config-$(uname -r)" 2>/dev/null | + grep -q "^CONFIG_MSEAL_SYSTEM_MAPPINGS=y"; then + CATEGORY="mmap" run_test ./sysmap_is_sealed +else + CATEGORY="mmap" run_test ./sysmap_is_sealed -T expect_sealed +fi CATEGORY="mlock" run_test ./mlock-random-test
tools/testing/selftests/mm/sysmap_is_sealed.c +22 -0
@@ -10,9 +10,11 @@#include <unistd.h> #include <string.h> #include <stdbool.h> +#include <sys/auxv.h> #include "kselftest.h" #include "kselftest_harness.h" +#include "vm_util.h" #define VMFLAGS "VmFlags:"#define MSEAL_FLAGS "sl"
@@ -46,6 +48,23 @@ bool mapping_is_sealed(char *name, FILE *maps)return false; } +static bool sysmaps_sealed (void ) +{ +void *vdso = (void *)getauxval (AT_SYSINFO_EHDR ); + +return vdso &&check_vmflag_sealed (vdso); +} + +/* +* Kernels built without CONFIG_MSEAL_SYSTEM_MAPPINGS don't seal system +* mappings either, so check_sealed skips if they aren't sealed. This fails +* instead, so run_vmtests.sh only runs it when that's set. +*/ +TEST (expect_sealed) +{ +EXPECT_TRUE (sysmaps_sealed ()); +} +FIXTURE (basic) {FILE *maps; };
@@ -107,6 +126,9 @@ FIXTURE_VARIANT_ADD(basic, stack) { TEST_F(basic, check_sealed) { +if (!sysmaps_sealed ()) +SKIP (return,"system mappings are not sealed" ); +if (!has_mapping (variant->name, self->maps)) {SKIP (return,"could not find the mapping, %s" , variant->name);
tools/testing/selftests/mm/vm_util.c +5 -0
@@ -785,6 +785,11 @@ bool check_vmflag_guard(void *addr)return check_vmflag (addr,"gu" ); } +bool check_vmflag_sealed (void *addr) +{ +return check_vmflag (addr,"sl" ); +} +bool softdirty_supported (void ) {char *addr;
tools/testing/selftests/mm/vm_util.h +1 -0
@@ -117,6 +117,7 @@ int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len,bool check_vmflag_io (void *addr);bool check_vmflag_pfnmap (void *addr);bool check_vmflag_guard (void *addr); +bool check_vmflag_sealed (void *addr);int open_procmap (pid_t pid,struct procmap_fd *procmap_out);int query_procmap (struct procmap_fd *procmap);bool find_vma_procmap (struct procmap_fd *procmap,void *address);
Signature · 1 line
2.54.0
PATCH v2 3/8 selftests/mm: Test that the seal on system mappings is enforced
In reply to Chris Downsysmap_is_sealed checks that system mappings are marked as sealed in smaps, but not that the seal is actually enforced. The rest of this series changes how system mappings get sealed, so let's make sure that is covered first.
Add tests that munmap(), mremap() and mprotect() of [vdso] fail with EPERM when it's sealed, and succeed when it isn't.
--- tools/testing/selftests/mm/sysmap_is_sealed.c | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+)
tools/testing/selftests/mm/sysmap_is_sealed.c +101 -0
@@ -11,6 +11,7 @@#include <string.h> #include <stdbool.h> #include <sys/auxv.h> +#include <sys/mman.h> #include "kselftest.h" #include "kselftest_harness.h"
@@ -138,4 +139,104 @@ TEST_F(basic, check_sealed) mapping_is_sealed(variant->name, self->maps)); }; +static void *find_vdso (struct procmap_fd *procmap,size_t *size) +{ +void *vdso = (void *)getauxval (AT_SYSINFO_EHDR ); + +if (!vdso || !find_vma_procmap (procmap, vdso)) +return NULL ; + *size = procmap->query.vma_end - procmap->query.vma_start; +return (void *)(uintptr_t )procmap->query.vma_start; +} + +/* +* When [vdso] isn't sealed, these leave libc with a vDSO which it can't use, +* which is fine since each test runs in its own child. Skip if AT_SYSINFO +* is set, since libc may also use the vDSO for syscalls. +*/ +TEST (vdso_munmap) +{ +struct procmap_fd procmap; +bool sealed; +size_t size; +void *vdso; +int ret; + +ASSERT_EQ (0 ,open_self_procmap (&procmap)); + vdso =find_vdso (&procmap, &size); +if (!vdso) +SKIP (return,"could not find the mapping, [vdso]" ); + sealed =check_vmflag_sealed (vdso); +if (!sealed &&getauxval (AT_SYSINFO )) +SKIP (return,"libc makes syscalls through the vDSO" ); + + ret =munmap (vdso, size); +if (sealed) { +EXPECT_EQ (-1 , ret); +EXPECT_EQ (EPERM , errno); + }else { +EXPECT_EQ (0 , ret); + } +EXPECT_EQ (sealed,find_vma_procmap (&procmap, vdso)); +close_procmap (&procmap); +} + +TEST (vdso_mremap) +{ +struct procmap_fd procmap; +void *vdso, *dst, *ret; +bool sealed; +size_t size; + +ASSERT_EQ (0 ,open_self_procmap (&procmap)); + vdso =find_vdso (&procmap, &size); +if (!vdso) +SKIP (return,"could not find the mapping, [vdso]" ); + sealed =check_vmflag_sealed (vdso); +if (!sealed &&getauxval (AT_SYSINFO )) +SKIP (return,"libc makes syscalls through the vDSO" ); + + dst =mmap (NULL , size,PROT_NONE ,MAP_PRIVATE |MAP_ANONYMOUS ,-1 ,0 ); +ASSERT_NE (MAP_FAILED , dst); + + ret =mremap (vdso, size, size,MREMAP_MAYMOVE |MREMAP_FIXED , dst); +if (sealed) { +EXPECT_EQ (MAP_FAILED , ret); +EXPECT_EQ (EPERM , errno); + }else { +EXPECT_EQ (dst, ret); + } +EXPECT_EQ (sealed,find_vma_procmap (&procmap, vdso)); +close_procmap (&procmap); +} + +TEST (vdso_mprotect) +{ +struct procmap_fd procmap; +bool sealed; +size_t size; +void *vdso; +int ret; + +ASSERT_EQ (0 ,open_self_procmap (&procmap)); + vdso =find_vdso (&procmap, &size); +if (!vdso) +SKIP (return,"could not find the mapping, [vdso]" ); + sealed =check_vmflag_sealed (vdso); +if (!sealed &&getauxval (AT_SYSINFO )) +SKIP (return,"libc makes syscalls through the vDSO" ); + + ret =mprotect (vdso, size,PROT_READ |PROT_WRITE ); +if (sealed) { +EXPECT_EQ (-1 , ret); +EXPECT_EQ (EPERM , errno); + }else { +EXPECT_EQ (0 , ret); + } +ASSERT_TRUE (find_vma_procmap (&procmap, vdso)); +EXPECT_EQ (sealed, + !(procmap.query.vma_flags &PROCMAP_QUERY_VMA_WRITABLE )); +close_procmap (&procmap); +} +TEST_HARNESS_MAIN
Signature · 1 line
2.54.0
PATCH v2 4/8 mm/mseal: Move MSEAL_SYSTEM_MAPPINGS to mm/Kconfig
In reply to Chris DownMSEAL_SYSTEM_MAPPINGS is in security/Kconfig, but it's an mm feature, it's implemented in mm, and changes to it need to go past mm.
Let's move it to mm/Kconfig, next to the other VMA features. No functional change.
--- mm/Kconfig | 21 +++++++++++++++++++++ security/Kconfig | 21 --------------------- 2 files changed, 21 insertions(+), 21 deletions(-)
mm/Kconfig +21 -0
@@ -1355,6 +1355,27 @@ config ANON_VMA_NAME area from being merged with adjacent virtual memory areas due to the difference in their name. +config MSEAL_SYSTEM_MAPPINGS + bool "mseal system mappings" + depends on 64BIT + depends on ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS + depends on !CHECKPOINT_RESTORE + help + Apply mseal on system mappings. + The system mappings includes vdso, vvar, vvar_vclock, + vectors (arm compat-mode), sigpage (arm compat-mode), uprobes. + + A 64-bit kernel is required for the memory sealing feature. + No specific hardware features from the CPU are needed. + + WARNING: This feature breaks programs which rely on relocating + or unmapping system mappings. Known broken software at the time + of writing includes CHECKPOINT_RESTORE, UML, gVisor, rr. Therefore + this config can't be enabled universally. + + For complete descriptions of memory sealing, please see + Documentation/userspace-api/mseal.rst + config HAVE_ARCH_USERFAULTFD_WP bool help
security/Kconfig +0 -21
@@ -49,27 +49,6 @@ config PROC_MEM_NO_FORCE endchoice -config MSEAL_SYSTEM_MAPPINGS - bool "mseal system mappings" - depends on 64BIT - depends on ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS - depends on !CHECKPOINT_RESTORE - help - Apply mseal on system mappings. - The system mappings includes vdso, vvar, vvar_vclock, - vectors (arm compat-mode), sigpage (arm compat-mode), uprobes. - - A 64-bit kernel is required for the memory sealing feature. - No specific hardware features from the CPU are needed. - - WARNING: This feature breaks programs which rely on relocating - or unmapping system mappings. Known broken software at the time - of writing includes CHECKPOINT_RESTORE, UML, gVisor, rr. Therefore - this config can't be enabled universally. - - For complete descriptions of memory sealing, please see - Documentation/userspace-api/mseal.rst - config SECURITY bool "Enable different security models" depends on SYSFS
Signature · 1 line
2.54.0
PATCH v2 5/8 uprobes/x86: Leave sealed trampolines in place on optimisation failure
In reply to Chris DownIf swbp_optimize() fails, __arch_uprobe_optimize() unmaps the trampoline it has just created. The next patch seals all special mappings when CONFIG_MSEAL_SYSTEM_MAPPINGS is enabled, which includes uprobe trampolines, and then this do_munmap() would fail with -EPERM and warn a second time.
Nothing is really leaked if we leave the trampoline mapped in that case, since get_uprobe_trampoline() reuses any existing trampoline which is in reach of the probe. So let's just leave it in place when it's sealed.
--- arch/x86/kernel/uprobes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kernel/uprobes.c +1 -1
@@ -1183,7 +1183,7 @@ static int __arch_uprobe_optimize(struct arch_uprobe *auprobe, struct mm_structif (IS_ERR (tramp))return PTR_ERR (tramp); ret =swbp_optimize (auprobe, vma, vaddr, tramp->vm_start); -if (WARN_ON_ONCE (ret) && new_mapping) +if (WARN_ON_ONCE (ret) && new_mapping && !vma_test (tramp,VMA_SEALED_BIT ))WARN_ON_ONCE (do_munmap (mm, tramp->vm_start,PAGE_SIZE ,NULL ));return ret; }
Signature · 1 line
2.54.0
PATCH v2 6/8 mm/mseal: Seal system mappings in __install_special_mapping()
In reply to Chris DownVM_SEALED_SYSMAP isn't really a VMA flag, it's an alias for VM_SEALED or VM_NONE depending on CONFIG_MSEAL_SYSTEM_MAPPINGS. Every caller of _install_special_mapping() on an architecture which supports sealing system mappings has to remember to pass it, and it keeps those callers on the legacy vm_flags_t API.
Let's instead have __install_special_mapping() seal the mapping itself when mseal_system_mappings() says so, and remove VM_SEALED_SYSMAP.
The only special mapping on these architectures which didn't already pass VM_SEALED_SYSMAP is the x86 uprobe trampoline, which is now sealed as well. The previous patch makes sure that's safe.
Since this seals all special mappings, the architecture opt-in now requires that none of them are moved or unmapped.
--- Documentation/userspace-api/mseal.rst | 3 ++- arch/arm64/kernel/vdso.c | 9 +++------ arch/loongarch/kernel/vdso.c | 3 +-- arch/riscv/kernel/vdso.c | 2 +- arch/s390/kernel/vdso.c | 2 +- arch/x86/entry/vdso/vma.c | 5 ++--- include/linux/mm.h | 6 ------ init/Kconfig | 5 ++--- kernel/events/uprobes.c | 2 +- lib/vdso/datastore.c | 2 +- mm/Kconfig | 3 ++- mm/vma.c | 2 ++ mm/vma.h | 5 +++++ 13 files changed, 23 insertions(+), 26 deletions(-)
Documentation/userspace-api/mseal.rst +2 -1
@@ -133,7 +133,8 @@ Use cases - System mappings: The system mappings are created by the kernel and include vdso, vvar, - vvar_vclock, vectors (arm compat-mode), sigpage (arm compat-mode), uprobes. + vvar_vclock, vectors (arm compat-mode), sigpage (arm compat-mode), uprobes, + uprobe trampolines (x86). Those system mappings are readonly only or execute only, memory sealing can protect them from ever changing to writable or unmapped/remapped as different
arch/arm64/kernel/vdso.c +3 -6
@@ -127,8 +127,7 @@ static int __setup_additional_pages(enum vdso_abi abi, mm->context.vdso = (void *)vdso_base; ret =_install_special_mapping (mm, vdso_base, vdso_text_len,VM_READ |VM_EXEC |gp_flags| -VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC | -VM_SEALED_SYSMAP , +VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC , vdso_info[abi].cm);if (IS_ERR (ret)) goto up_fail;
@@ -254,8 +253,7 @@ static int aarch32_kuser_helpers_setup(struct mm_struct *mm)*/ ret =_install_special_mapping (mm,AARCH32_VECTORS_BASE ,PAGE_SIZE ,VM_READ |VM_EXEC | -VM_MAYREAD |VM_MAYEXEC | -VM_SEALED_SYSMAP , +VM_MAYREAD |VM_MAYEXEC , &aarch32_vdso_maps[AA32_MAP_VECTORS ]);return PTR_ERR_OR_ZERO (ret);
@@ -278,8 +276,7 @@ static int aarch32_sigreturn_setup(struct mm_struct *mm)*/ ret =_install_special_mapping (mm, addr,PAGE_SIZE ,VM_READ |VM_EXEC |VM_MAYREAD | -VM_MAYWRITE |VM_MAYEXEC | -VM_SEALED_SYSMAP , +VM_MAYWRITE |VM_MAYEXEC , &aarch32_vdso_maps[AA32_MAP_SIGPAGE ]);if (IS_ERR (ret)) goto out;
arch/loongarch/kernel/vdso.c +1 -2
@@ -109,8 +109,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) vdso_addr = data_addr +VVAR_SIZE ; vma =_install_special_mapping (mm, vdso_addr, info->size,VM_READ |VM_EXEC | -VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC | -VM_SEALED_SYSMAP , +VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC , &info->code_mapping);if (IS_ERR (vma)) { ret =PTR_ERR (vma);
arch/riscv/kernel/vdso.c +1 -1
@@ -140,7 +140,7 @@ static int __setup_additional_pages(struct mm_struct *mm, ret =_install_special_mapping (mm, vdso_base, vdso_text_len, - (VM_READ |VM_EXEC |VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC |VM_SEALED_SYSMAP ), + (VM_READ |VM_EXEC |VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC ), vdso_info->cm);if (IS_ERR (ret))
arch/s390/kernel/vdso.c +1 -1
@@ -65,7 +65,7 @@ static int map_vdso(unsigned long addr, unsigned long vdso_mapping_len) vdso_text_start = vvar_start +VDSO_NR_PAGES *PAGE_SIZE ;/* VM_MAYWRITE for COW so gdb can set breakpoints */ vma =_install_special_mapping (mm, vdso_text_start, vdso_text_len, -VM_READ |VM_EXEC |VM_SEALED_SYSMAP | +VM_READ |VM_EXEC |VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC , &vdso_mapping);if (IS_ERR (vma)) {
arch/x86/entry/vdso/vma.c +2 -3
@@ -178,8 +178,7 @@ static int map_vdso(const struct vdso_image *image, unsigned long addr) text_start, image->size,VM_READ |VM_EXEC | -VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC | -VM_SEALED_SYSMAP , +VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC , &vdso_mapping);if (IS_ERR (vma)) {
@@ -198,7 +197,7 @@ static int map_vdso(const struct vdso_image *image, unsigned long addr)VDSO_VCLOCK_PAGES_START (addr),VDSO_NR_VCLOCK_PAGES *PAGE_SIZE ,VM_READ |VM_MAYREAD |VM_IO |VM_DONTDUMP | -VM_PFNMAP |VM_SEALED_SYSMAP , +VM_PFNMAP , &vvar_vclock_mapping);if (IS_ERR (vma)) {
include/linux/mm.h +0 -6
@@ -567,12 +567,6 @@ enum {/* Temporary until VMA flags conversion complete. */ #define VM_STACK_FLAGS vma_flags_to_legacy(VMA_STACK_FLAGS) -#ifdef CONFIG_MSEAL_SYSTEM_MAPPINGS -#define VM_SEALED_SYSMAP VM_SEALED -#else -#define VM_SEALED_SYSMAP VM_NONE -#endif -/* VMA basic access permission flags */ #define VM_ACCESS_FLAGS (VM_READ | VM_WRITE | VM_EXEC)#define VMA_ACCESS_FLAGS mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT)
init/Kconfig +2 -3
@@ -1955,9 +1955,8 @@ config ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS A 64-bit kernel is required for the memory sealing feature. No specific hardware features from the CPU are needed. - To enable this feature, the architecture needs to update their - special mappings calls to include the sealing flag and confirm - that it doesn't unmap/remap system mappings during the life + To enable this feature, the architecture needs to confirm that it + doesn't unmap/remap any of its special mappings during the life time of the process. The existence of this flag for an architecture implies that it does not require the remapping of the system mappings during process lifetime, so sealing these mappings is safe
kernel/events/uprobes.c +1 -1
@@ -1727,7 +1727,7 @@ static int xol_add_vma(struct mm_struct *mm, struct xol_area *area) vma =_install_special_mapping (mm, area->vaddr,PAGE_SIZE ,VM_EXEC |VM_MAYEXEC |VM_DONTCOPY | -VM_MIXEDMAP |VM_SEALED_SYSMAP , +VM_MIXEDMAP , &xol_mapping);if (IS_ERR (vma)) { ret =PTR_ERR (vma);
lib/vdso/datastore.c +1 -1
@@ -124,6 +124,6 @@ struct vm_area_struct *vdso_install_vvar_mapping(struct mm_struct *mm, unsigned {return _install_special_mapping (mm, addr,VDSO_NR_PAGES *PAGE_SIZE ,VM_READ |VM_MAYREAD |VM_DONTDUMP | -VM_MIXEDMAP |VM_SEALED_SYSMAP , +VM_MIXEDMAP , &vdso_vvar_mapping); }
mm/Kconfig +2 -1
@@ -1363,7 +1363,8 @@ config MSEAL_SYSTEM_MAPPINGS help Apply mseal on system mappings. The system mappings includes vdso, vvar, vvar_vclock, - vectors (arm compat-mode), sigpage (arm compat-mode), uprobes. + vectors (arm compat-mode), sigpage (arm compat-mode), uprobes, + uprobe trampolines (x86). A 64-bit kernel is required for the memory sealing feature. No specific hardware features from the CPU are needed.
mm/vma.c +2 -0
@@ -3705,6 +3705,8 @@ struct vm_area_struct *__install_special_mapping(vma_flags_set_mask (&vma_flags, mm->def_vma_flags);vma_flags_set (&vma_flags,VMA_DONTEXPAND_BIT ); +if (mseal_system_mappings ()) +vma_flags_set (&vma_flags,VMA_SEALED_BIT );if (pgtable_supports_soft_dirty ())vma_flags_set (&vma_flags,VMA_SOFTDIRTY_BIT );vma_flags_clear_mask (&vma_flags,VMA_LOCKED_MASK );
mm/vma.h +5 -0
@@ -800,6 +800,11 @@ static inline bool vma_is_sealed(struct vm_area_struct *vma) }#endif +static inline bool mseal_system_mappings (void ) +{ +return IS_ENABLED (CONFIG_MSEAL_SYSTEM_MAPPINGS ); +} +#if defined(CONFIG_STACK_GROWSUP )int expand_upwards (struct vm_area_struct *vma,unsigned long address);#endif
Signature · 1 line
2.54.0
PATCH v2 7/8 mm/mseal: Add no_mseal_sys_mappings boot parameter
In reply to Chris DownCONFIG_MSEAL_SYSTEM_MAPPINGS seals each process' system mappings, such as the vDSO, when they are created. These are read-only or execute-only mappings which every process trusts, so sealing them means that a memory corruption bug which can pass a bad pointer to mprotect(), mremap() or munmap() can't be used to make them writable, or to move or replace them.
However, sealing can only be chosen at build time, and it breaks software which moves or modifies these mappings. This is not ideal, since hosts which want sealing and hosts which run such software then can't share a kernel.
For example, at Meta, we build sealing into the kernel for our VM hosts, and we'd like it on as much of the rest of the fleet as possible. The problem is that the rest of the fleet also runs things which it breaks. As one example among many, Reverie (which our deterministic execution tool, Hermit, is built on) patches the vDSO of the processes it traces by injecting an mprotect() [0]. So right now the hosts which want sealing need their own kernel image, and this option is one of the things which forces that.
So, to improve this, let's add no_mseal_sys_mappings. That way one kernel can be built with CONFIG_MSEAL_SYSTEM_MAPPINGS=y, keep sealing on by default, and turn it off only on the hosts which need it. Kernels built without the option are unchanged.
Since sealing can now be turned off at boot, MSEAL_SYSTEM_MAPPINGS also no longer needs to depend on !CHECKPOINT_RESTORE. The option is off by default and already documents that it breaks CRIU, so let's drop the dependency. For the same reason, the documentation no longer needs to say that it can't be enabled universally.
--- Documentation/admin-guide/kernel-parameters.txt | 7 +++++++ Documentation/userspace-api/mseal.rst | 9 +++++---- mm/Kconfig | 15 ++++++++------- mm/mseal.c | 12 ++++++++++++ mm/vma.h | 5 ++++- 5 files changed, 36 insertions(+), 12 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +7 -0
@@ -4603,6 +4603,13 @@ Kernel parameters nomodule Disable module load + no_mseal_sys_mappings + [KNL,EARLY] With CONFIG_MSEAL_SYSTEM_MAPPINGS, don't + seal system mappings such as the vDSO. Sealing them + breaks software which moves, unmaps or modifies them, + such as CHECKPOINT_RESTORE (CRIU), UML, gVisor and rr. + See Documentation/userspace-api/mseal.rst. + nonmi_ipi [X86] Disable using NMI IPIs during panic/reboot to shutdown the other cpus. Instead use the REBOOT_VECTOR irq.
Documentation/userspace-api/mseal.rst +5 -4
@@ -143,12 +143,13 @@ Use cases If supported by an architecture (CONFIG_ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS), the CONFIG_MSEAL_SYSTEM_MAPPINGS seals all system mappings of this - architecture. + architecture. The ``no_mseal_sys_mappings`` kernel command line parameter + turns this off. WARNING: This feature breaks programs which rely on relocating - or unmapping system mappings. Known broken software at the time - of writing includes CHECKPOINT_RESTORE, UML, gVisor, rr. Therefore - this config can't be enabled universally. + or unmapping system mappings. Software known to break with it + at the time of writing includes CHECKPOINT_RESTORE, UML, gVisor + and rr. When not to use mseal =====================
mm/Kconfig +8 -7
@@ -1359,24 +1359,25 @@ config MSEAL_SYSTEM_MAPPINGS bool "mseal system mappings" depends on 64BIT depends on ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS - depends on !CHECKPOINT_RESTORE help Apply mseal on system mappings. The system mappings includes vdso, vvar, vvar_vclock, vectors (arm compat-mode), sigpage (arm compat-mode), uprobes, uprobe trampolines (x86). - A 64-bit kernel is required for the memory sealing feature. - No specific hardware features from the CPU are needed. - WARNING: This feature breaks programs which rely on relocating - or unmapping system mappings. Known broken software at the time - of writing includes CHECKPOINT_RESTORE, UML, gVisor, rr. Therefore - this config can't be enabled universally. + or unmapping system mappings. Software known to break with it + at the time of writing includes CHECKPOINT_RESTORE, UML, gVisor + and rr. + + With this configuration option set, the feature can be disabled + with the no_mseal_sys_mappings boot parameter. For complete descriptions of memory sealing, please see Documentation/userspace-api/mseal.rst + If unsure, say N. + config HAVE_ARCH_USERFAULTFD_WP bool help
mm/mseal.c +12 -0
@@ -7,6 +7,7 @@* Author: Jeff Xu <[email protected]> */ +#include <linux/init.h> #include <linux/mempolicy.h> #include <linux/minmax.h> #include <linux/mman.h>
@@ -16,6 +17,17 @@#include <linux/sched.h> #include "internal.h" +#ifdef CONFIG_MSEAL_SYSTEM_MAPPINGS +bool mseal_system_mappings_disabled__ro_after_init ; + +static int __init early_no_mseal_sys_mappings (char *buf) +{ + mseal_system_mappings_disabled = true; +return 0 ; +} +early_param ("no_mseal_sys_mappings" , early_no_mseal_sys_mappings); +#endif +static bool range_contains_unmapped (unsigned long start,unsigned long end) {VMA_ITERATOR (vmi, current->mm, start);
mm/vma.h +4 -1
@@ -800,9 +800,12 @@ static inline bool vma_is_sealed(struct vm_area_struct *vma) }#endif +extern bool mseal_system_mappings_disabled; +static inline bool mseal_system_mappings (void ) { -return IS_ENABLED (CONFIG_MSEAL_SYSTEM_MAPPINGS ); +return IS_ENABLED (CONFIG_MSEAL_SYSTEM_MAPPINGS ) && + !mseal_system_mappings_disabled; }#if defined(CONFIG_STACK_GROWSUP )
Signature · 1 line
2.54.0
PATCH v2 8/8 selftests/mm: Check that no_mseal_sys_mappings unseals system mappings
In reply to Chris DownBooting with no_mseal_sys_mappings currently makes check_sealed skip and expect_sealed fail, so nothing checks that the parameter actually turns sealing off.
So let's check /proc/cmdline for it, and when it's there, expect [vdso] and the other system mappings to be unsealed rather than skipping or failing. The vdso tests already expect munmap(), mremap() and mprotect() to succeed when [vdso] isn't sealed, so that covers what the parameter allows as well.
--- tools/testing/selftests/mm/sysmap_is_sealed.c | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-)
tools/testing/selftests/mm/sysmap_is_sealed.c +21 -4
@@ -56,6 +56,21 @@ static bool sysmaps_sealed(void)return vdso &&check_vmflag_sealed (vdso); } +static bool sysmaps_disabled (void ) +{ +char cmdline[4096 ]; +bool disabled = false; +FILE *f; + + f =fopen ("/proc/cmdline" ,"r" ); +if (!f) +return false; +if (fgets (cmdline,sizeof (cmdline), f)) + disabled =strstr (cmdline,"no_mseal_sys_mappings" ); +fclose (f); +return disabled; +} +/* * Kernels built without CONFIG_MSEAL_SYSTEM_MAPPINGS don't seal system * mappings either, so check_sealed skips if they aren't sealed. This fails
@@ -63,7 +78,7 @@ static bool sysmaps_sealed(void)*/ TEST (expect_sealed) { -EXPECT_TRUE (sysmaps_sealed ()); +EXPECT_EQ (!sysmaps_disabled (),sysmaps_sealed ()); }FIXTURE (basic) {
@@ -127,7 +142,9 @@ FIXTURE_VARIANT_ADD(basic, stack) { TEST_F(basic, check_sealed) { -if (!sysmaps_sealed ()) +bool disabled =sysmaps_disabled (); + +if (!disabled && !sysmaps_sealed ())SKIP (return,"system mappings are not sealed" );if (!has_mapping (variant->name, self->maps)) {
@@ -135,8 +152,8 @@ TEST_F(basic, check_sealed) variant->name); } -EXPECT_EQ (variant->sealed, -mapping_is_sealed (variant->name, self->maps)); +EXPECT_EQ (variant->sealed && !disabled, +mapping_is_sealed (variant->name, self->maps)); };static void *find_vdso (struct procmap_fd *procmap,size_t *size)
Signature · 1 line
2.54.0
Quoted 4 lines · Chris Down
System mapping sealing is currently a build time choice. Hosts which want it and hosts which need to move or unmap these mappings therefore need separate kernels, which is not ideal. Let's allow the administrator to choose at boot time.
Thanks Chris for the updates and for adding the unsealed boot parameter test coverage! The v2 series looks good to me.