Otherwise the code races with munmap (causing a use-after-free of the vma) or with close (causing a use-after-free of the struct file). The bug was introduced by commit 90ed52ebe481 ("[PATCH] holepunch: fix mmap_sem i_mutex deadlock")
Unified · Split
| @@ -15,6 +15,7 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <linux/sched.h> |
| 16 | 16 | #include <linux/ksm.h> |
| 17 | 17 | #include <linux/fs.h> |
| 18 | +#include <linux/file.h> | |
| 18 | 19 | |
| 19 | 20 | /* |
| 20 | 21 | * Any behaviour which results in changes to the vma->vm_flags needs to |
| @@ -204,14 +205,16 @@ static long madvise_remove(struct vm_area_struct *vma, · 183 unmodified lines | ||
| 204 | 205 | { |
| 205 | 206 | loff_t offset; |
| 206 | 207 | int error; |
| 208 | + struct file *f; | |
| 207 | 209 | |
| 208 | 210 | *prev = NULL; /* tell sys_madvise we drop mmap_sem */ |
| 209 | 211 | |
| 210 | 212 | if (vma->vm_flags & (VM_LOCKED|VM_NONLINEAR|VM_HUGETLB)) |
| 211 | 213 | return -EINVAL; |
| 212 | 214 | |
| 213 | − if (!vma->vm_file || !vma->vm_file->f_mapping | |
| 214 | − || !vma->vm_file->f_mapping->host) { | |
| 215 | + f = vma->vm_file; | |
| 216 | + | |
| 217 | + if (!f || !f->f_mapping || !f->f_mapping->host) { | |
| 215 | 218 | return -EINVAL; |
| 216 | 219 | } |
| 217 | 220 | |
| @@ -221,11 +224,18 @@ static long madvise_remove(struct vm_area_struct *vma, · 3 unmodified lines | ||
| 221 | 224 | offset = (loff_t)(start - vma->vm_start) |
| 222 | 225 | + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); |
| 223 | 226 | |
| 224 | − /* filesystem's fallocate may need to take i_mutex */ | |
| 227 | + /* | |
| 228 | + * Filesystem's fallocate may need to take i_mutex. We need to | |
| 229 | + * explicitly grab a reference because the vma (and hence the | |
| 230 | + * vma's reference to the file) can go away as soon as we drop | |
| 231 | + * mmap_sem. | |
| 232 | + */ | |
| 233 | + get_file(f); | |
| 225 | 234 | up_read(¤t->mm->mmap_sem); |
| 226 | − error = do_fallocate(vma->vm_file, | |
| 235 | + error = do_fallocate(f, | |
| 227 | 236 | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, |
| 228 | 237 | offset, end - start); |
| 238 | + fput(f); | |
| 229 | 239 | down_read(¤t->mm->mmap_sem); |
| 230 | 240 | return error; |
| 231 | 241 | } |