1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Implement mseal() syscall. 4 * 5 * Copyright (c) 2023,2024 Google, Inc. 6 * 7 * Author: Jeff Xu <jeffxu@chromium.org> 8 */ 9 10 #include <linux/mempolicy.h> 11 #include <linux/minmax.h> 12 #include <linux/mman.h> 13 #include <linux/mm.h> 14 #include <linux/mm_inline.h> 15 #include <linux/syscalls.h> 16 #include <linux/sched.h> 17 #include "internal.h" 18 19 static bool range_contains_unmapped(unsigned long start, unsigned long end) 20 { 21 VMA_ITERATOR(vmi, current->mm, start); 22 unsigned long prev_end = start; 23 struct vm_area_struct *vma; 24 25 for_each_vma_range(vmi, vma, end) { 26 if (vma->vm_start > prev_end) 27 return true; 28 29 prev_end = vma->vm_end; 30 } 31 32 return prev_end < end; 33 } 34 35 static int __mseal_range(unsigned long start, unsigned long end) 36 { 37 VMA_ITERATOR(vmi, current->mm, start); 38 struct vm_area_struct *vma, *prev; 39 40 /* We know there are no gaps so this will be non-NULL. */ 41 vma = vma_iter_load(&vmi); 42 prev = vma_prev(&vmi); 43 if (start > vma->vm_start) 44 prev = vma; 45 46 for_each_vma_range(vmi, vma, end) { 47 const unsigned long curr_start = max(vma->vm_start, start); 48 const unsigned long curr_end = min(vma->vm_end, end); 49 50 if (!vma_test(vma, VMA_SEALED_BIT)) { 51 vma_flags_t vma_flags = vma->flags; 52 53 vma_flags_set(&vma_flags, VMA_SEALED_BIT); 54 55 vma = vma_modify_flags(&vmi, prev, vma, curr_start, 56 curr_end, &vma_flags); 57 if (IS_ERR(vma)) 58 return PTR_ERR(vma); 59 vma_start_write(vma); 60 vma_set_flags(vma, VMA_SEALED_BIT); 61 } 62 63 prev = vma; 64 } 65 66 return 0; 67 } 68 69 static int mseal_range(unsigned long start, unsigned long end) 70 { 71 int err; 72 73 err = mmap_write_lock_killable(current->mm); 74 if (err) 75 return err; 76 if (range_contains_unmapped(start, end)) 77 err = -ENOMEM; 78 else 79 err = __mseal_range(start, end); 80 mmap_write_unlock(current->mm); 81 return err; 82 } 83 84 /** 85 * mseal_mmap_page_zero() - If the MMAP_PAGE_ZERO personality is set, mseal() 86 * the page mapped at address zero. 87 */ 88 void mseal_mmap_page_zero(void) 89 { 90 int err; 91 92 if (WARN_ON_ONCE(!(current->personality & MMAP_PAGE_ZERO))) 93 return; 94 95 err = mseal_range(0, PAGE_SIZE); 96 if (err) 97 pr_warn_ratelimited("pid=%d, couldn't seal address 0, ret=%d.\n", 98 task_pid_nr(current), err); 99 } 100 101 /* 102 * Seal VMAs in the specified input range to prevent an attacker replacing what 103 * is mapped in the range with something else. 104 * 105 * Disallows: 106 * - VMA unmapping, remapping or shrinking. 107 * - Overwriting the VMA with another one via mmap(), mremap() or similar. 108 * - Alteration of properties via mprotect()/pkey_mprotect(). 109 * - Destructive madvise() behaviours (like MADV_DONTNEED) on anonymous read-only 110 * ranges. 111 * 112 * Since unmapped ranges can be mapped at any time, the input range must span 113 * mapped ranges only. 114 * 115 * The flags parameter is currently reserved. 116 */ 117 SYSCALL_DEFINE3(mseal, unsigned long, start, size_t, len, unsigned long, flags) 118 { 119 size_t len_aligned; 120 unsigned long end; 121 122 /* Verify flags not set. */ 123 if (flags) 124 return -EINVAL; 125 126 start = untagged_addr(start); 127 if (!PAGE_ALIGNED(start)) 128 return -EINVAL; 129 130 len_aligned = PAGE_ALIGN(len); 131 /* Check to see whether len was rounded up from small -ve to zero. */ 132 if (len && !len_aligned) 133 return -EINVAL; 134 135 end = start + len_aligned; 136 if (end < start) 137 return -EINVAL; 138 139 if (end == start) 140 return 0; 141 142 return mseal_range(start, end); 143 } 144