Skip to content

Commit a7031f1

Browse files
mjguzikakpm00
authored andcommitted
kernel/fork: stop playing lockless games for exe_file replacement
xchg originated in 6e399cd ("prctl: avoid using mmap_sem for exe_file serialization"). While the commit message does not explain *why* the change, I found the original submission [1] which ultimately claims it cleans things up by removing dependency of exe_file on the semaphore. However, fe69d56 ("kernel/fork: always deny write access to current MM exe_file") added a semaphore up/down cycle to synchronize the state of exe_file against fork, defeating the point of the original change. This is on top of semaphore trips already present both in the replacing function and prctl (the only consumer). Normally replacing exe_file does not happen for busy processes, thus write-locking is not an impediment to performance in the intended use case. If someone keeps invoking the routine for a busy processes they are trying to play dirty and that's another reason to avoid any trickery. As such I think the atomic here only adds complexity for no benefit. Just write-lock around the replacement. I also note that replacement races against the mapping check loop as nothing synchronizes actual assignment with with said checks but I am not addressing it in this patch. (Is the loop of any use to begin with?) Link: https://lore.kernel.org/linux-mm/[email protected]/ [1] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Mateusz Guzik <[email protected]> Acked-by: Oleg Nesterov <[email protected]> Acked-by: David Hildenbrand <[email protected]> Cc: "Christian Brauner (Microsoft)" <[email protected]> Cc: Davidlohr Bueso <[email protected]> Cc: Eric W. Biederman <[email protected]> Cc: Konstantin Khlebnikov <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Mateusz Guzik <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 8bd49ef commit a7031f1

File tree

2 files changed

+11
-15
lines changed

2 files changed

+11
-15
lines changed

fs/exec.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,8 +1276,8 @@ int begin_new_exec(struct linux_binprm * bprm)
12761276

12771277
/*
12781278
* Must be called _before_ exec_mmap() as bprm->mm is
1279-
* not visible until then. This also enables the update
1280-
* to be lockless.
1279+
* not visible until then. Doing it here also ensures
1280+
* we don't race against replace_mm_exe_file().
12811281
*/
12821282
retval = set_mm_exe_file(bprm->mm, bprm->file);
12831283
if (retval)

kernel/fork.c

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,8 +1396,8 @@ EXPORT_SYMBOL_GPL(mmput_async);
13961396
* This changes mm's executable file (shown as symlink /proc/[pid]/exe).
13971397
*
13981398
* Main users are mmput() and sys_execve(). Callers prevent concurrent
1399-
* invocations: in mmput() nobody alive left, in execve task is single
1400-
* threaded.
1399+
* invocations: in mmput() nobody alive left, in execve it happens before
1400+
* the new mm is made visible to anyone.
14011401
*
14021402
* Can only fail if new_exe_file != NULL.
14031403
*/
@@ -1432,9 +1432,7 @@ int set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
14321432
/**
14331433
* replace_mm_exe_file - replace a reference to the mm's executable file
14341434
*
1435-
* This changes mm's executable file (shown as symlink /proc/[pid]/exe),
1436-
* dealing with concurrent invocation and without grabbing the mmap lock in
1437-
* write mode.
1435+
* This changes mm's executable file (shown as symlink /proc/[pid]/exe).
14381436
*
14391437
* Main user is sys_prctl(PR_SET_MM_MAP/EXE_FILE).
14401438
*/
@@ -1464,22 +1462,20 @@ int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
14641462
return ret;
14651463
}
14661464

1467-
/* set the new file, lockless */
14681465
ret = deny_write_access(new_exe_file);
14691466
if (ret)
14701467
return -EACCES;
14711468
get_file(new_exe_file);
14721469

1473-
old_exe_file = xchg(&mm->exe_file, new_exe_file);
1470+
/* set the new file */
1471+
mmap_write_lock(mm);
1472+
old_exe_file = rcu_dereference_raw(mm->exe_file);
1473+
rcu_assign_pointer(mm->exe_file, new_exe_file);
1474+
mmap_write_unlock(mm);
1475+
14741476
if (old_exe_file) {
1475-
/*
1476-
* Don't race with dup_mmap() getting the file and disallowing
1477-
* write access while someone might open the file writable.
1478-
*/
1479-
mmap_read_lock(mm);
14801477
allow_write_access(old_exe_file);
14811478
fput(old_exe_file);
1482-
mmap_read_unlock(mm);
14831479
}
14841480
return 0;
14851481
}

0 commit comments

Comments
 (0)