| @@ -1164,6 +1164,75 @@ out_unlock: · 1163 unmodified lines |
| 1164 | 1164 | return err; |
| 1165 | 1165 | } |
| 1166 | 1166 | |
| 1167 | +int mrwprotect_range(struct userfaultfd_ctx *ctx, unsigned long start, |
| 1168 | + unsigned long len, bool enable_rwp) |
| 1169 | +{ |
| 1170 | + struct mm_struct *dst_mm = ctx->mm; |
| 1171 | + unsigned long end = start + len; |
| 1172 | + struct vm_area_struct *dst_vma; |
| 1173 | + unsigned int mm_cp_flags; |
| 1174 | + struct mmu_gather tlb; |
| 1175 | + bool found = false; |
| 1176 | + VMA_ITERATOR(vmi, dst_mm, start); |
| 1177 | + |
| 1178 | + VM_WARN_ON_ONCE(start & ~PAGE_MASK); |
| 1179 | + VM_WARN_ON_ONCE(len & ~PAGE_MASK); |
| 1180 | + VM_WARN_ON_ONCE(start + len <= start); |
| 1181 | + |
| 1182 | + guard(mmap_read_lock)(dst_mm); |
| 1183 | + guard(rwsem_read)(&ctx->map_changing_lock); |
| 1184 | + |
| 1185 | + if (atomic_read(&ctx->mmap_changing)) |
| 1186 | + return -EAGAIN; |
| 1187 | + |
| 1188 | + if (enable_rwp) |
| 1189 | + mm_cp_flags = MM_CP_UFFD_RWP; |
| 1190 | + else |
| 1191 | + mm_cp_flags = MM_CP_UFFD_RWP_RESOLVE; |
| 1192 | + |
| 1193 | + /* |
| 1194 | + * Pre-scan the range: validate every spanned VMA before applying |
| 1195 | + * any change_protection() so a partial failure cannot leave the |
| 1196 | + * process with only a prefix of the range re-protected. |
| 1197 | + */ |
| 1198 | + for_each_vma_range(vmi, dst_vma, end) { |
| 1199 | + if (!userfaultfd_rwp(dst_vma)) |
| 1200 | + return -ENOENT; |
| 1201 | + |
| 1202 | + if (is_vm_hugetlb_page(dst_vma)) { |
| 1203 | + unsigned long page_mask; |
| 1204 | + |
| 1205 | + page_mask = vma_kernel_pagesize(dst_vma) - 1; |
| 1206 | + if ((start & page_mask) || (len & page_mask)) |
| 1207 | + return -EINVAL; |
| 1208 | + } |
| 1209 | + found = true; |
| 1210 | + } |
| 1211 | + if (!found) |
| 1212 | + return -ENOENT; |
| 1213 | + |
| 1214 | + vma_iter_set(&vmi, start); |
| 1215 | + tlb_gather_mmu(&tlb, dst_mm); |
| 1216 | + for_each_vma_range(vmi, dst_vma, end) { |
| 1217 | + unsigned long vma_start = max(dst_vma->vm_start, start); |
| 1218 | + unsigned long vma_end = min(dst_vma->vm_end, end); |
| 1219 | + unsigned int flags = mm_cp_flags; |
| 1220 | + |
| 1221 | + /* |
| 1222 | + * On resolve, try to upgrade writability per-VMA -- |
| 1223 | + * MM_CP_TRY_CHANGE_WRITABLE WARNs in |
| 1224 | + * maybe_change_pte_writable() if the VMA is not VM_WRITE, |
| 1225 | + * and RWP can be registered on PROT_READ-only mappings. |
| 1226 | + */ |
| 1227 | + if (!enable_rwp && vma_wants_manual_pte_write_upgrade(dst_vma)) |
| 1228 | + flags |= MM_CP_TRY_CHANGE_WRITABLE; |
| 1229 | + |
| 1230 | + change_protection(&tlb, dst_vma, vma_start, vma_end, flags); |
| 1231 | + } |
| 1232 | + tlb_finish_mmu(&tlb); |
| 1233 | + |
| 1234 | + return 0; |
| 1235 | +} |
| 1167 | 1236 | |
| 1168 | 1237 | void double_pt_lock(spinlock_t *ptl1, |
| 1169 | 1238 | spinlock_t *ptl2) |
| @@ -2207,9 +2276,22 @@ static struct vm_area_struct *userfaultfd_clear_vma(struct vma_iterator *vmi, · 1037 unmodified lines |
| 2207 | 2276 | if (start == vma->vm_start && end == vma->vm_end) |
| 2208 | 2277 | give_up_on_oom = true; |
| 2209 | 2278 | |
| 2210 | | − /* Reset ptes for the whole vma range if wr-protected */ |
| 2211 | | − if (userfaultfd_wp(vma)) |
| 2212 | | − uffd_wp_range(vma, start, end - start, false); |
| 2279 | + /* Clear the uffd bit and/or restore protnone PTEs */ |
| 2280 | + if (userfaultfd_protected(vma)) { |
| 2281 | + unsigned int mm_cp_flags = 0; |
| 2282 | + struct mmu_gather tlb; |
| 2283 | + |
| 2284 | + if (userfaultfd_wp(vma)) |
| 2285 | + mm_cp_flags |= MM_CP_UFFD_WP_RESOLVE; |
| 2286 | + if (userfaultfd_rwp(vma)) |
| 2287 | + mm_cp_flags |= MM_CP_UFFD_RWP_RESOLVE; |
| 2288 | + if (vma_wants_manual_pte_write_upgrade(vma)) |
| 2289 | + mm_cp_flags |= MM_CP_TRY_CHANGE_WRITABLE; |
| 2290 | + |
| 2291 | + tlb_gather_mmu(&tlb, vma->vm_mm); |
| 2292 | + change_protection(&tlb, vma, start, end, mm_cp_flags); |
| 2293 | + tlb_finish_mmu(&tlb); |
| 2294 | + } |
| 2213 | 2295 | |
| 2214 | 2296 | ret = vma_modify_flags_uffd(vmi, prev, vma, start, end, |
| 2215 | 2297 | &new_vma_flags, NULL_VM_UFFD_CTX, |
| @@ -2258,6 +2340,14 @@ static int userfaultfd_register_range(struct userfaultfd_ctx *ctx, · 42 unmodified lines |
| 2258 | 2340 | vma_test_all_mask(vma, vma_flags)) |
| 2259 | 2341 | goto skip; |
| 2260 | 2342 | |
| 2343 | + /* |
| 2344 | + * Pre-scan in userfaultfd_register() already rejected mode |
| 2345 | + * switches that would drop VM_UFFD_WP or VM_UFFD_RWP, so a |
| 2346 | + * stray bit here is a bug. |
| 2347 | + */ |
| 2348 | + VM_WARN_ON_ONCE(vma->vm_userfaultfd_ctx.ctx == ctx && |
| 2349 | + vma->vm_flags & (VM_UFFD_WP | VM_UFFD_RWP) & ~vm_flags); |
| 2350 | + |
| 2261 | 2351 | if (vma->vm_start > start) |
| 2262 | 2352 | start = vma->vm_start; |
| 2263 | 2353 | vma_end = min(end, vma->vm_end); |
| @@ -2524,6 +2614,8 @@ static inline struct uffd_msg userfault_msg(unsigned long address, · 260 unmodified lines |
| 2524 | 2614 | msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_WRITE; |
| 2525 | 2615 | if (reason & VM_UFFD_WP) |
| 2526 | 2616 | msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_WP; |
| 2617 | + if (reason & VM_UFFD_RWP) |
| 2618 | + msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_RWP; |
| 2527 | 2619 | if (reason & VM_UFFD_MINOR) |
| 2528 | 2620 | msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_MINOR; |
| 2529 | 2621 | if (features & UFFD_FEATURE_THREAD_ID) |
| @@ -3623,6 +3715,22 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, · 1093 unmodified lines |
| 3623 | 3715 | |
| 3624 | 3716 | vm_flags |= VM_UFFD_WP; |
| 3625 | 3717 | } |
| 3718 | + if (uffdio_register.mode & UFFDIO_REGISTER_MODE_RWP) { |
| 3719 | + if (!pgtable_supports_uffd() || VM_UFFD_RWP == VM_NONE) |
| 3720 | + goto out; |
| 3721 | + if (!(ctx->features & UFFD_FEATURE_RWP)) |
| 3722 | + goto out; |
| 3723 | + vm_flags |= VM_UFFD_RWP; |
| 3724 | + } |
| 3725 | + |
| 3726 | + /* |
| 3727 | + * WP and RWP share the uffd PTE bit and |
| 3728 | + * cannot coexist in the same VMA — the bit would carry ambiguous |
| 3729 | + * semantics. Reject the combination up front. |
| 3730 | + */ |
| 3731 | + if ((vm_flags & VM_UFFD_WP) && (vm_flags & VM_UFFD_RWP)) |
| 3732 | + goto out; |
| 3733 | + |
| 3626 | 3734 | if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MINOR) { |
| 3627 | 3735 | #ifndef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR |
| 3628 | 3736 | goto out; |
| @@ -3677,6 +3785,17 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, · 48 unmodified lines |
| 3677 | 3785 | if (!vma_can_userfault(cur, vm_flags, wp_async)) |
| 3678 | 3786 | goto out_unlock; |
| 3679 | 3787 | |
| 3788 | + /* |
| 3789 | + * RWP uses protnone as an access-tracking marker. PROT_NONE |
| 3790 | + * VMAs have vm_page_prot == PAGE_NONE, so RWP resolution |
| 3791 | + * cannot make a page accessible again. Reject at register |
| 3792 | + * time only: a VMA that later becomes inaccessible via |
| 3793 | + * mprotect() must still be unregisterable, so this is not |
| 3794 | + * part of vma_can_userfault(). |
| 3795 | + */ |
| 3796 | + if ((vm_flags & VM_UFFD_RWP) && !vma_is_accessible(cur)) |
| 3797 | + goto out_unlock; |
| 3798 | + |
| 3680 | 3799 | /* |
| 3681 | 3800 | * UFFDIO_COPY will fill file holes even without |
| 3682 | 3801 | * PROT_WRITE. This check enforces that if this is a |
| @@ -3716,6 +3835,16 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, · 33 unmodified lines |
| 3716 | 3835 | cur->vm_userfaultfd_ctx.ctx != ctx) |
| 3717 | 3836 | goto out_unlock; |
| 3718 | 3837 | |
| 3838 | + /* |
| 3839 | + * Mode switches that drop VM_UFFD_WP or VM_UFFD_RWP would |
| 3840 | + * leave PTE markers without the flag that describes them; |
| 3841 | + * subsequent mprotect() would then promote stale markers |
| 3842 | + * into the other mode. Require an unregister first. |
| 3843 | + */ |
| 3844 | + if (cur->vm_userfaultfd_ctx.ctx == ctx && |
| 3845 | + cur->vm_flags & (VM_UFFD_WP | VM_UFFD_RWP) & ~vm_flags) |
| 3846 | + goto out_unlock; |
| 3847 | + |
| 3719 | 3848 | /* |
| 3720 | 3849 | * Note vmas containing huge pages |
| 3721 | 3850 | */ |
| @@ -3749,6 +3878,10 @@ out_unlock: · 27 unmodified lines |
| 3749 | 3878 | if (!(uffdio_register.mode & UFFDIO_REGISTER_MODE_MINOR)) |
| 3750 | 3879 | ioctls_out &= ~((__u64)1 << _UFFDIO_CONTINUE); |
| 3751 | 3880 | |
| 3881 | + /* RWPROTECT is only supported for RWP ranges */ |
| 3882 | + if (!(uffdio_register.mode & UFFDIO_REGISTER_MODE_RWP)) |
| 3883 | + ioctls_out &= ~((__u64)1 << _UFFDIO_RWPROTECT); |
| 3884 | + |
| 3752 | 3885 | /* |
| 3753 | 3886 | * Now that we scanned all vmas we can already tell |
| 3754 | 3887 | * userland which ioctls methods are guaranteed to |
| @@ -4096,6 +4229,55 @@ static int userfaultfd_writeprotect(struct userfaultfd_ctx *ctx, · 341 unmodified lines |
| 4096 | 4229 | return ret; |
| 4097 | 4230 | } |
| 4098 | 4231 | |
| 4232 | +static int userfaultfd_rwprotect(struct userfaultfd_ctx *ctx, |
| 4233 | + unsigned long arg) |
| 4234 | +{ |
| 4235 | + int ret; |
| 4236 | + struct uffdio_rwprotect uffdio_rwp; |
| 4237 | + struct userfaultfd_wake_range range; |
| 4238 | + bool mode_rwp, mode_dontwake; |
| 4239 | + |
| 4240 | + if (atomic_read(&ctx->mmap_changing)) |
| 4241 | + return -EAGAIN; |
| 4242 | + |
| 4243 | + if (copy_from_user(&uffdio_rwp, (void __user *)arg, |
| 4244 | + sizeof(uffdio_rwp))) |
| 4245 | + return -EFAULT; |
| 4246 | + |
| 4247 | + ret = validate_range(ctx->mm, uffdio_rwp.range.start, |
| 4248 | + uffdio_rwp.range.len); |
| 4249 | + if (ret) |
| 4250 | + return ret; |
| 4251 | + |
| 4252 | + if (uffdio_rwp.mode & ~(UFFDIO_RWPROTECT_MODE_DONTWAKE | |
| 4253 | + UFFDIO_RWPROTECT_MODE_RWP)) |
| 4254 | + return -EINVAL; |
| 4255 | + |
| 4256 | + mode_rwp = uffdio_rwp.mode & UFFDIO_RWPROTECT_MODE_RWP; |
| 4257 | + mode_dontwake = uffdio_rwp.mode & UFFDIO_RWPROTECT_MODE_DONTWAKE; |
| 4258 | + |
| 4259 | + if (mode_rwp && mode_dontwake) |
| 4260 | + return -EINVAL; |
| 4261 | + |
| 4262 | + if (mmget_not_zero(ctx->mm)) { |
| 4263 | + ret = mrwprotect_range(ctx, uffdio_rwp.range.start, |
| 4264 | + uffdio_rwp.range.len, mode_rwp); |
| 4265 | + mmput(ctx->mm); |
| 4266 | + } else { |
| 4267 | + return -ESRCH; |
| 4268 | + } |
| 4269 | + |
| 4270 | + if (ret) |
| 4271 | + return ret; |
| 4272 | + |
| 4273 | + if (!mode_rwp && !mode_dontwake) { |
| 4274 | + range.start = uffdio_rwp.range.start; |
| 4275 | + range.len = uffdio_rwp.range.len; |
| 4276 | + wake_userfault(ctx, &range); |
| 4277 | + } |
| 4278 | + return ret; |
| 4279 | +} |
| 4280 | + |
| 4099 | 4281 | static int userfaultfd_continue(struct userfaultfd_ctx *ctx, unsigned long arg) |
| 4100 | 4282 | { |
| 4101 | 4283 | __s64 ret; |
| @@ -4402,6 +4584,9 @@ static long userfaultfd_ioctl(struct file *file, unsigned cmd, · 300 unmodified lines |
| 4402 | 4584 | case UFFDIO_POISON: |
| 4403 | 4585 | ret = userfaultfd_poison(ctx, arg); |
| 4404 | 4586 | break; |
| 4587 | + case UFFDIO_RWPROTECT: |
| 4588 | + ret = userfaultfd_rwprotect(ctx, arg); |
| 4589 | + break; |
| 4405 | 4590 | } |
| 4406 | 4591 | return ret; |
| 4407 | 4592 | } |