fput() assumes that it can't be called after exit_task_work() but
this is not true, for example free_ipc_ns()->shm_destroy() can do
this. In this case fput() silently leaks the file.

Change it to fallback to delayed_fput_work if task_work_add() fails.
The patch looks complicated but it is not, it changes the code from

        if (PF_KTHREAD) {
                schedule_work(...);
                return;
        }
        task_work_add(...)

to
        if (!PF_KTHREAD) {
                if (!task_work_add(...))
                        return;
                /* fallback */
        }
        schedule_work(...);

As for shm_destroy() in particular, we could make another fix but I
think this change makes sense anyway. There could be another similar
user, it is not safe to assume that task_work_add() can't fail.

Reported-by: Andrey Vagin <[email protected]>
Signed-off-by: Oleg Nesterov <[email protected]>
---
 fs/file_table.c |   19 ++++++++++---------
 1 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/fs/file_table.c b/fs/file_table.c
index cd4d87a..485dc0e 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -306,17 +306,18 @@ void fput(struct file *file)
 {
        if (atomic_long_dec_and_test(&file->f_count)) {
                struct task_struct *task = current;
+               unsigned long flags;
+
                file_sb_list_del(file);
-               if (unlikely(in_interrupt() || task->flags & PF_KTHREAD)) {
-                       unsigned long flags;
-                       spin_lock_irqsave(&delayed_fput_lock, flags);
-                       list_add(&file->f_u.fu_list, &delayed_fput_list);
-                       schedule_work(&delayed_fput_work);
-                       spin_unlock_irqrestore(&delayed_fput_lock, flags);
-                       return;
+               if (likely(!in_interrupt() && !(task->flags & PF_KTHREAD))) {
+                       init_task_work(&file->f_u.fu_rcuhead, ____fput);
+                       if (!task_work_add(task, &file->f_u.fu_rcuhead, true))
+                               return;
                }
-               init_task_work(&file->f_u.fu_rcuhead, ____fput);
-               task_work_add(task, &file->f_u.fu_rcuhead, true);
+               spin_lock_irqsave(&delayed_fput_lock, flags);
+               list_add(&file->f_u.fu_list, &delayed_fput_list);
+               schedule_work(&delayed_fput_work);
+               spin_unlock_irqrestore(&delayed_fput_lock, flags);
        }
 }
 
-- 
1.5.5.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to