Pull MAP_DENYWRITE removal from David Hildenbrand: "Remove all in-tree usage of MAP_DENYWRITE from the kernel and remove VM_DENYWRITE. There are some (minor) user-visible changes: - We no longer deny write access to shared libaries loaded via legacy uselib(); this behavior matches modern user space e.g. dlopen(). - We no longer deny write access to the elf interpreter after exec completed, treating it just like shared libraries (which it often is). - We always deny write access to the file linked via /proc/pid/exe: sys_prctl(PR_SET_MM_MAP/EXE_FILE) will fail if write access to the file cannot be denied, and write access to the file will remain denied until the link is effectivel gone (exec, termination, sys_prctl(PR_SET_MM_MAP/EXE_FILE)) -- just as if exec'ing the file. Cross-compiled for a bunch of architectures (alpha, microblaze, i386, s390x, ...) and verified via ltp that especially the relevant tests (i.e., creat07 and execve04) continue working as expected" * tag 'denywrite-for-5.15' of git://github.com/davidhildenbrand/linux: fs: update documentation of get_write_access() and friends mm: ignore MAP_DENYWRITE in ksys_mmap_pgoff() mm: remove VM_DENYWRITE binfmt: remove in-tree usage of MAP_DENYWRITE kernel/fork: always deny write access to current MM exe_file kernel/fork: factor out replacing the current MM exe_file binfmt: don't use MAP_DENYWRITE when loading shared libraries via uselib()
Showing the changes to kernel (3 of 16 files). Show all
Unified · Split
| @@ -8320,8 +8320,6 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) · 8319 unmodified lines | ||
| 8320 | 8320 | else |
| 8321 | 8321 | flags = MAP_PRIVATE; |
| 8322 | 8322 | |
| 8323 | − if (vma->vm_flags & VM_DENYWRITE) | |
| 8324 | − flags |= MAP_DENYWRITE; | |
| 8325 | 8323 | if (vma->vm_flags & VM_LOCKED) |
| 8326 | 8324 | flags |= MAP_LOCKED; |
| 8327 | 8325 | if (is_vm_hugetlb_page(vma)) |
| @@ -471,6 +471,20 @@ void free_task(struct task_struct *tsk) · 470 unmodified lines | ||
| 471 | 471 | } |
| 472 | 472 | EXPORT_SYMBOL(free_task); |
| 473 | 473 | |
| 474 | +static void dup_mm_exe_file(struct mm_struct *mm, struct mm_struct *oldmm) | |
| 475 | +{ | |
| 476 | + struct file *exe_file; | |
| 477 | + | |
| 478 | + exe_file = get_mm_exe_file(oldmm); | |
| 479 | + RCU_INIT_POINTER(mm->exe_file, exe_file); | |
| 480 | + /* | |
| 481 | + * We depend on the oldmm having properly denied write access to the | |
| 482 | + * exe_file already. | |
| 483 | + */ | |
| 484 | + if (exe_file && deny_write_access(exe_file)) | |
| 485 | + pr_warn_once("deny_write_access() failed in %s\n", __func__); | |
| 486 | +} | |
| 487 | + | |
| 474 | 488 | #ifdef CONFIG_MMU |
| 475 | 489 | static __latent_entropy int dup_mmap(struct mm_struct *mm, |
| 476 | 490 | struct mm_struct *oldmm) |
| @@ -494,7 +508,7 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm, · 17 unmodified lines | ||
| 494 | 508 | mmap_write_lock_nested(mm, SINGLE_DEPTH_NESTING); |
| 495 | 509 | |
| 496 | 510 | /* No ordering required: file already has been exposed. */ |
| 497 | − RCU_INIT_POINTER(mm->exe_file, get_mm_exe_file(oldmm)); | |
| 511 | + dup_mm_exe_file(mm, oldmm); | |
| 498 | 512 | |
| 499 | 513 | mm->total_vm = oldmm->total_vm; |
| 500 | 514 | mm->data_vm = oldmm->data_vm; |
| @@ -557,12 +571,9 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm, · 56 unmodified lines | ||
| 557 | 571 | tmp->vm_flags &= ~(VM_LOCKED | VM_LOCKONFAULT); |
| 558 | 572 | file = tmp->vm_file; |
| 559 | 573 | if (file) { |
| 560 | − struct inode *inode = file_inode(file); | |
| 561 | 574 | struct address_space *mapping = file->f_mapping; |
| 562 | 575 | |
| 563 | 576 | get_file(file); |
| 564 | − if (tmp->vm_flags & VM_DENYWRITE) | |
| 565 | − put_write_access(inode); | |
| 566 | 577 | i_mmap_lock_write(mapping); |
| 567 | 578 | if (tmp->vm_flags & VM_SHARED) |
| 568 | 579 | mapping_allow_writable(mapping); |
| @@ -640,7 +651,7 @@ static inline void mm_free_pgd(struct mm_struct *mm) · 71 unmodified lines | ||
| 640 | 651 | static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) |
| 641 | 652 | { |
| 642 | 653 | mmap_write_lock(oldmm); |
| 643 | − RCU_INIT_POINTER(mm->exe_file, get_mm_exe_file(oldmm)); | |
| 654 | + dup_mm_exe_file(mm, oldmm); | |
| 644 | 655 | mmap_write_unlock(oldmm); |
| 645 | 656 | return 0; |
| 646 | 657 | } |
| @@ -1150,11 +1161,11 @@ void mmput_async(struct mm_struct *mm) · 503 unmodified lines | ||
| 1150 | 1161 | * |
| 1151 | 1162 | * Main users are mmput() and sys_execve(). Callers prevent concurrent |
| 1152 | 1163 | * invocations: in mmput() nobody alive left, in execve task is single |
| 1153 | − * threaded. sys_prctl(PR_SET_MM_MAP/EXE_FILE) also needs to set the | |
| 1154 | − * mm->exe_file, but does so without using set_mm_exe_file() in order | |
| 1155 | − * to avoid the need for any locks. | |
| 1164 | + * threaded. | |
| 1165 | + * | |
| 1166 | + * Can only fail if new_exe_file != NULL. | |
| 1156 | 1167 | */ |
| 1157 | −void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) | |
| 1168 | +int set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) | |
| 1158 | 1169 | { |
| 1159 | 1170 | struct file *old_exe_file; |
| 1160 | 1171 | |
| @@ -1165,11 +1176,73 @@ void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) · 4 unmodified lines | ||
| 1165 | 1176 | */ |
| 1166 | 1177 | old_exe_file = rcu_dereference_raw(mm->exe_file); |
| 1167 | 1178 | |
| 1168 | − if (new_exe_file) | |
| 1179 | + if (new_exe_file) { | |
| 1180 | + /* | |
| 1181 | + * We expect the caller (i.e., sys_execve) to already denied | |
| 1182 | + * write access, so this is unlikely to fail. | |
| 1183 | + */ | |
| 1184 | + if (unlikely(deny_write_access(new_exe_file))) | |
| 1185 | + return -EACCES; | |
| 1169 | 1186 | get_file(new_exe_file); |
| 1187 | + } | |
| 1170 | 1188 | rcu_assign_pointer(mm->exe_file, new_exe_file); |
| 1171 | − if (old_exe_file) | |
| 1189 | + if (old_exe_file) { | |
| 1190 | + allow_write_access(old_exe_file); | |
| 1172 | 1191 | fput(old_exe_file); |
| 1192 | + } | |
| 1193 | + return 0; | |
| 1194 | +} | |
| 1195 | + | |
| 1196 | +/** | |
| 1197 | + * replace_mm_exe_file - replace a reference to the mm's executable file | |
| 1198 | + * | |
| 1199 | + * This changes mm's executable file (shown as symlink /proc/[pid]/exe), | |
| 1200 | + * dealing with concurrent invocation and without grabbing the mmap lock in | |
| 1201 | + * write mode. | |
| 1202 | + * | |
| 1203 | + * Main user is sys_prctl(PR_SET_MM_MAP/EXE_FILE). | |
| 1204 | + */ | |
| 1205 | +int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) | |
| 1206 | +{ | |
| 1207 | + struct vm_area_struct *vma; | |
| 1208 | + struct file *old_exe_file; | |
| 1209 | + int ret = 0; | |
| 1210 | + | |
| 1211 | + /* Forbid mm->exe_file change if old file still mapped. */ | |
| 1212 | + old_exe_file = get_mm_exe_file(mm); | |
| 1213 | + if (old_exe_file) { | |
| 1214 | + mmap_read_lock(mm); | |
| 1215 | + for (vma = mm->mmap; vma && !ret; vma = vma->vm_next) { | |
| 1216 | + if (!vma->vm_file) | |
| 1217 | + continue; | |
| 1218 | + if (path_equal(&vma->vm_file->f_path, | |
| 1219 | + &old_exe_file->f_path)) | |
| 1220 | + ret = -EBUSY; | |
| 1221 | + } | |
| 1222 | + mmap_read_unlock(mm); | |
| 1223 | + fput(old_exe_file); | |
| 1224 | + if (ret) | |
| 1225 | + return ret; | |
| 1226 | + } | |
| 1227 | + | |
| 1228 | + /* set the new file, lockless */ | |
| 1229 | + ret = deny_write_access(new_exe_file); | |
| 1230 | + if (ret) | |
| 1231 | + return -EACCES; | |
| 1232 | + get_file(new_exe_file); | |
| 1233 | + | |
| 1234 | + old_exe_file = xchg(&mm->exe_file, new_exe_file); | |
| 1235 | + if (old_exe_file) { | |
| 1236 | + /* | |
| 1237 | + * Don't race with dup_mmap() getting the file and disallowing | |
| 1238 | + * write access while someone might open the file writable. | |
| 1239 | + */ | |
| 1240 | + mmap_read_lock(mm); | |
| 1241 | + allow_write_access(old_exe_file); | |
| 1242 | + fput(old_exe_file); | |
| 1243 | + mmap_read_unlock(mm); | |
| 1244 | + } | |
| 1245 | + return 0; | |
| 1173 | 1246 | } |
| 1174 | 1247 | |
| 1175 | 1248 | /** |
| @@ -1847,7 +1847,6 @@ SYSCALL_DEFINE1(umask, int, mask) · 1846 unmodified lines | ||
| 1847 | 1847 | static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd) |
| 1848 | 1848 | { |
| 1849 | 1849 | struct fd exe; |
| 1850 | − struct file *old_exe, *exe_file; | |
| 1851 | 1850 | struct inode *inode; |
| 1852 | 1851 | int err; |
| 1853 | 1852 | |
| @@ -1870,40 +1869,10 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd) · 16 unmodified lines | ||
| 1870 | 1869 | if (err) |
| 1871 | 1870 | goto exit; |
| 1872 | 1871 | |
| 1873 | − /* | |
| 1874 | − * Forbid mm->exe_file change if old file still mapped. | |
| 1875 | − */ | |
| 1876 | − exe_file = get_mm_exe_file(mm); | |
| 1877 | − err = -EBUSY; | |
| 1878 | − if (exe_file) { | |
| 1879 | − struct vm_area_struct *vma; | |
| 1880 | − | |
| 1881 | − mmap_read_lock(mm); | |
| 1882 | − for (vma = mm->mmap; vma; vma = vma->vm_next) { | |
| 1883 | − if (!vma->vm_file) | |
| 1884 | − continue; | |
| 1885 | − if (path_equal(&vma->vm_file->f_path, | |
| 1886 | − &exe_file->f_path)) | |
| 1887 | − goto exit_err; | |
| 1888 | − } | |
| 1889 | − | |
| 1890 | − mmap_read_unlock(mm); | |
| 1891 | − fput(exe_file); | |
| 1892 | − } | |
| 1893 | − | |
| 1894 | − err = 0; | |
| 1895 | − /* set the new file, lockless */ | |
| 1896 | − get_file(exe.file); | |
| 1897 | − old_exe = xchg(&mm->exe_file, exe.file); | |
| 1898 | − if (old_exe) | |
| 1899 | − fput(old_exe); | |
| 1872 | + err = replace_mm_exe_file(mm, exe.file); | |
| 1900 | 1873 | exit: |
| 1901 | 1874 | fdput(exe); |
| 1902 | 1875 | return err; |
| 1903 | −exit_err: | |
| 1904 | − mmap_read_unlock(mm); | |
| 1905 | − fput(exe_file); | |
| 1906 | − goto exit; | |
| 1907 | 1876 | } |
| 1908 | 1877 | |
| 1909 | 1878 | /* |