The get_task_exe_file() function locks the given task with task_lock() which when used inside audit_exe_compare() can cause deadlocks on systems that generate audit records when the task_lock() is held. As we should be able to safely access current->mm we can drop the call to get_task_exe_file() and get a reference to current->mm directly which we can then use in a call to get_mm_exe_file() without having to take task_lock().
While the audit_exe_compare() function takes an arbitrary task_struct parameter, all of the callers outside of fork/clone, call audit_exe_compare() to operate on the current task_struct, making it the common case. In the fork/clone case, when audit_exe_compare() is called on the child task_struct, it should be safe to use the get_task_mm() function as no other task should be holding task_lock() at this time. Cc: <[email protected]> Fixes: 5efc244346f9 ("audit: fix exe_file access in audit_exe_compare") Reported-by: Andreas Steinmetz <[email protected]> Signed-off-by: Paul Moore <[email protected]> --- kernel/audit_watch.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 65075f1e4ac8..fa3e6ea0e58c 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -526,8 +526,20 @@ int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark) struct file *exe_file; unsigned long ino; dev_t dev; + struct mm_struct *mm; - exe_file = get_task_exe_file(tsk); + /* almost always operate on current, exceptions for fork/clone */ + if (likely(tsk == current)) { + mmget(current->mm); + mm = current->mm; + } else { + /* this can deadlock outside the fork/clone usage (see above) */ + mm = get_task_mm(tsk); + if (!mm) + return 0; + } + exe_file = get_mm_exe_file(mm); + mmput(mm); if (!exe_file) return 0; ino = file_inode(exe_file)->i_ino; -- 2.42.0
