summaryrefslogtreecommitdiff
path: root/kernel/fork.c
diff options
context:
space:
mode:
authorDavid Hildenbrand <david@redhat.com>2021-04-23 10:20:25 +0200
committerDavid Hildenbrand <david@redhat.com>2021-09-03 18:42:01 +0200
commit35d7bdc86031a2c1ae05ac27dfa93b2acdcbaecc (patch)
tree0637f57df13ec0ff4bfe4c2aabf7a91640a42668 /kernel/fork.c
parent42be8b42535183f84df99acbaf799e38724348f3 (diff)
kernel/fork: factor out replacing the current MM exe_file
Let's factor the main logic out into replace_mm_exe_file(), such that all mm->exe_file logic is contained in kernel/fork.c. While at it, perform some simple cleanups that are possible now that we're simplifying the individual functions. Acked-by: Christian Brauner <christian.brauner@ubuntu.com> Acked-by: "Eric W. Biederman" <ebiederm@xmission.com> Acked-by: Christian König <christian.koenig@amd.com> Signed-off-by: David Hildenbrand <david@redhat.com>
Diffstat (limited to 'kernel/fork.c')
-rw-r--r--kernel/fork.c44
1 files changed, 41 insertions, 3 deletions
diff --git a/kernel/fork.c b/kernel/fork.c
index 44f4c2d83763..f4ac883c4a1e 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1148,9 +1148,7 @@ void mmput_async(struct mm_struct *mm)
*
* Main users are mmput() and sys_execve(). Callers prevent concurrent
* invocations: in mmput() nobody alive left, in execve task is single
- * threaded. sys_prctl(PR_SET_MM_MAP/EXE_FILE) also needs to set the
- * mm->exe_file, but does so without using set_mm_exe_file() in order
- * to avoid the need for any locks.
+ * threaded.
*/
void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
{
@@ -1171,6 +1169,46 @@ void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
}
/**
+ * replace_mm_exe_file - replace a reference to the mm's executable file
+ *
+ * This changes mm's executable file (shown as symlink /proc/[pid]/exe),
+ * dealing with concurrent invocation and without grabbing the mmap lock in
+ * write mode.
+ *
+ * Main user is sys_prctl(PR_SET_MM_MAP/EXE_FILE).
+ */
+int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
+{
+ struct vm_area_struct *vma;
+ struct file *old_exe_file;
+ int ret = 0;
+
+ /* Forbid mm->exe_file change if old file still mapped. */
+ old_exe_file = get_mm_exe_file(mm);
+ if (old_exe_file) {
+ mmap_read_lock(mm);
+ for (vma = mm->mmap; vma && !ret; vma = vma->vm_next) {
+ if (!vma->vm_file)
+ continue;
+ if (path_equal(&vma->vm_file->f_path,
+ &old_exe_file->f_path))
+ ret = -EBUSY;
+ }
+ mmap_read_unlock(mm);
+ fput(old_exe_file);
+ if (ret)
+ return ret;
+ }
+
+ /* set the new file, lockless */
+ get_file(new_exe_file);
+ old_exe_file = xchg(&mm->exe_file, new_exe_file);
+ if (old_exe_file)
+ fput(old_exe_file);
+ return 0;
+}
+
+/**
* get_mm_exe_file - acquire a reference to the mm's executable file
*
* Returns %NULL if mm has no associated executable file.