xref: /linux/Documentation/userspace-api/mseal.rst (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
1.. SPDX-License-Identifier: GPL-2.0
2
3=====================
4Introduction of mseal
5=====================
6
7:Author: Jeff Xu <jeffxu@chromium.org>
8
9Modern CPUs support memory permissions such as RW and NX bits. The memory
10permission feature improves security stance on memory corruption bugs, i.e.
11the attacker can’t just write to arbitrary memory and point the code to it,
12the memory has to be marked with X bit, or else an exception will happen.
13
14Memory sealing additionally protects the mapping itself against
15modifications. This is useful to mitigate memory corruption issues where a
16corrupted pointer is passed to a memory management system. For example,
17such an attacker primitive can break control-flow integrity guarantees
18since read-only memory that is supposed to be trusted can become writable
19or .text pages can get remapped. Memory sealing can automatically be
20applied by the runtime loader to seal .text and .rodata pages and
21applications can additionally seal security critical data at runtime.
22
23A similar feature already exists in the XNU kernel with the
24VM_FLAGS_PERMANENT flag [1] and on OpenBSD with the mimmutable syscall [2].
25
26SYSCALL
27=======
28mseal syscall signature
29-----------------------
30   ``int mseal(void *addr, size_t len, unsigned long flags)``
31
32   **addr**/**len**: virtual memory address range.
33      The address range set by **addr**/**len** must meet:
34         - The start address must be in an allocated VMA.
35         - The start address must be page aligned.
36         - The end address (**addr** + **len**) must be in an allocated VMA.
37         - no gap (unallocated memory) between start and end address.
38
39      The ``len`` will be paged aligned implicitly by the kernel.
40
41   **flags**: reserved for future use.
42
43   **Return values**:
44      - **0**: Success.
45      - **-EINVAL**:
46         * Invalid input ``flags``.
47         * The start address (``addr``) is not page aligned.
48         * Address range (``addr`` + ``len``) overflow.
49      - **-ENOMEM**:
50         * The start address (``addr``) is not allocated.
51         * The end address (``addr`` + ``len``) is not allocated.
52         * A gap (unallocated memory) between start and end address.
53      - **-ENOSYS**:
54         * The kernel does not implement ``mseal()``.
55
56   **Note about error return**:
57      - For above error cases, users can expect the given memory range is
58        unmodified, i.e. no partial update.
59      - There might be other internal errors/cases not listed here, e.g.
60        error during merging/splitting VMAs, or the process reaching the maximum
61        number of supported VMAs. In those cases, partial updates to the given
62        memory range could happen. However, those cases should be rare.
63
64   **Architecture support**:
65      mseal is built only for 64-bit kernels. 32-bit kernels return
66      ``-ENOSYS``.
67
68   **Idempotent**:
69      users can call mseal multiple times. mseal on an already sealed memory
70      is a no-action (not error).
71
72   **no munseal**
73      Once mapping is sealed, it can't be unsealed. The kernel should never
74      have munseal, this is consistent with other sealing feature, e.g.
75      F_SEAL_SEAL for file.
76
77Blocked mm syscall for sealed mapping
78-------------------------------------
79   It might be important to note: **once the mapping is sealed, it will
80   stay in the process's memory until the process terminates**.
81
82   Example::
83
84         *ptr = mmap(0, 4096, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
85         rc = mseal(ptr, 4096, 0);
86         /* munmap will fail */
87         rc = munmap(ptr, 4096);
88         assert(rc < 0);
89
90   Blocked mm syscall:
91      - munmap
92      - mmap
93      - mremap
94      - mprotect and pkey_mprotect
95      - some destructive madvise behaviors: MADV_DONTNEED, MADV_FREE,
96        MADV_DONTNEED_LOCKED, MADV_FREE, MADV_DONTFORK, MADV_WIPEONFORK
97
98   The first set of syscalls to block is munmap, mremap, mmap. They can
99   either leave an empty space in the address space, therefore allowing
100   replacement with a new mapping with new set of attributes, or can
101   overwrite the existing mapping with another mapping.
102
103   mprotect and pkey_mprotect are blocked because they changes the
104   protection bits (RWX) of the mapping.
105
106   Certain destructive madvise behaviors, specifically MADV_DONTNEED,
107   MADV_FREE, MADV_DONTNEED_LOCKED, and MADV_WIPEONFORK, can introduce
108   risks when applied to anonymous memory by threads lacking write
109   permissions. Consequently, these operations are prohibited under such
110   conditions. The aforementioned behaviors have the potential to modify
111   region contents by discarding pages, effectively performing a memset(0)
112   operation on the anonymous memory.
113
114   Kernel will return -EPERM for blocked syscalls.
115
116   When blocked syscall return -EPERM due to sealing, the memory regions may
117   or may not be changed, depends on the syscall being blocked:
118
119      - munmap: munmap is atomic. If one of VMAs in the given range is
120        sealed, none of VMAs are updated.
121      - mprotect, pkey_mprotect, madvise: partial update might happen, e.g.
122        when mprotect over multiple VMAs, mprotect might update the beginning
123        VMAs before reaching the sealed VMA and return -EPERM.
124      - mmap and mremap: undefined behavior.
125
126Use cases
127=========
128- glibc:
129  The dynamic linker, during loading ELF executables, can apply sealing to
130  mapping segments.
131
132- Chrome browser: protect some security sensitive data structures.
133
134- System mappings:
135  The system mappings are created by the kernel and include vdso, vvar,
136  vvar_vclock, vectors (arm compat-mode), sigpage (arm compat-mode), uprobes.
137
138  Those system mappings are readonly only or execute only, memory sealing can
139  protect them from ever changing to writable or unmapped/remapped as different
140  attributes. This is useful to mitigate memory corruption issues where a
141  corrupted pointer is passed to a memory management system.
142
143  If supported by an architecture (CONFIG_ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS),
144  the CONFIG_MSEAL_SYSTEM_MAPPINGS seals all system mappings of this
145  architecture.
146
147  WARNING: This feature breaks programs which rely on relocating
148  or unmapping system mappings. Known broken software at the time
149  of writing includes CHECKPOINT_RESTORE, UML, gVisor, rr. Therefore
150  this config can't be enabled universally.
151
152When not to use mseal
153=====================
154Applications can apply sealing to any virtual memory region from userspace,
155but it is *crucial to thoroughly analyze the mapping's lifetime* prior to
156apply the sealing. This is because the sealed mapping *won’t be unmapped*
157until the process terminates or the exec system call is invoked.
158
159For example:
160   - aio/shm
161     aio/shm can call mmap and  munmap on behalf of userspace, e.g.
162     ksys_shmdt() in shm.c. The lifetimes of those mapping are not tied to
163     the lifetime of the process. If those memories are sealed from userspace,
164     then munmap will fail, causing leaks in VMA address space during the
165     lifetime of the process.
166
167   - ptr allocated by malloc (heap)
168     Don't use mseal on the memory ptr return from malloc().
169     malloc() is implemented by allocator, e.g. by glibc. Heap manager might
170     allocate a ptr from brk or mapping created by mmap.
171     If an app calls mseal on a ptr returned from malloc(), this can affect
172     the heap manager's ability to manage the mappings; the outcome is
173     non-deterministic.
174
175     Example::
176
177        ptr = malloc(size);
178        /* don't call mseal on ptr return from malloc. */
179        mseal(ptr, size);
180        /* free will success, allocator can't shrink heap lower than ptr */
181        free(ptr);
182
183mseal doesn't block
184===================
185In a nutshell, mseal blocks certain mm syscall from modifying some of VMA's
186attributes, such as protection bits (RWX). Sealed mappings doesn't mean the
187memory is immutable.
188
189As Jann Horn pointed out in [3], there are still a few ways to write
190to RO memory, which is, in a way, by design. And those could be blocked
191by different security measures.
192
193Those cases are:
194
195   - Write to read-only memory through /proc/self/mem interface (FOLL_FORCE).
196   - Write to read-only memory through ptrace (such as PTRACE_POKETEXT).
197   - userfaultfd.
198
199The idea that inspired this patch comes from Stephen Röttger’s work in V8
200CFI [4]. Chrome browser in ChromeOS will be the first user of this API.
201
202Reference
203=========
204- [1] https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/osfmk/mach/vm_statistics.h#L274
205- [2] https://man.openbsd.org/mimmutable.2
206- [3] https://lore.kernel.org/lkml/CAG48ez3ShUYey+ZAFsU2i1RpQn0a5eOs2hzQ426FkcgnfUGLvA@mail.gmail.com
207- [4] https://docs.google.com/document/d/1O2jwK4dxI3nRcOJuPYkonhTkNQfbmwdvxQMyXgeaRHo/edit#heading=h.bvaojj9fu6hc
208