Skip to content

Commit e7b2c40

Browse files
oleg-nesterovAl Viro
authored and
Al Viro
committed
fput: task_work_add() can fail if the caller has passed exit_task_work()
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 <avagin@openvz.org> Signed-off-by: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent 698b822 commit e7b2c40

File tree

1 file changed

+10
-9
lines changed

1 file changed

+10
-9
lines changed

fs/file_table.c

+10-9
Original file line numberDiff line numberDiff line change
@@ -306,17 +306,18 @@ void fput(struct file *file)
306306
{
307307
if (atomic_long_dec_and_test(&file->f_count)) {
308308
struct task_struct *task = current;
309+
unsigned long flags;
310+
309311
file_sb_list_del(file);
310-
if (unlikely(in_interrupt() || task->flags & PF_KTHREAD)) {
311-
unsigned long flags;
312-
spin_lock_irqsave(&delayed_fput_lock, flags);
313-
list_add(&file->f_u.fu_list, &delayed_fput_list);
314-
schedule_work(&delayed_fput_work);
315-
spin_unlock_irqrestore(&delayed_fput_lock, flags);
316-
return;
312+
if (likely(!in_interrupt() && !(task->flags & PF_KTHREAD))) {
313+
init_task_work(&file->f_u.fu_rcuhead, ____fput);
314+
if (!task_work_add(task, &file->f_u.fu_rcuhead, true))
315+
return;
317316
}
318-
init_task_work(&file->f_u.fu_rcuhead, ____fput);
319-
task_work_add(task, &file->f_u.fu_rcuhead, true);
317+
spin_lock_irqsave(&delayed_fput_lock, flags);
318+
list_add(&file->f_u.fu_list, &delayed_fput_list);
319+
schedule_work(&delayed_fput_work);
320+
spin_unlock_irqrestore(&delayed_fput_lock, flags);
320321
}
321322
}
322323

0 commit comments

Comments
 (0)