Commit e7b2c406 authored by Oleg Nesterov's avatar Oleg Nesterov Committed by Al Viro

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: default avatarAndrey Vagin <avagin@openvz.org>
Signed-off-by: default avatarOleg Nesterov <oleg@redhat.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 698b8223
...@@ -306,17 +306,18 @@ void fput(struct file *file) ...@@ -306,17 +306,18 @@ void fput(struct file *file)
{ {
if (atomic_long_dec_and_test(&file->f_count)) { if (atomic_long_dec_and_test(&file->f_count)) {
struct task_struct *task = current; struct task_struct *task = current;
file_sb_list_del(file);
if (unlikely(in_interrupt() || task->flags & PF_KTHREAD)) {
unsigned long flags; unsigned long flags;
file_sb_list_del(file);
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;
}
spin_lock_irqsave(&delayed_fput_lock, flags); spin_lock_irqsave(&delayed_fput_lock, flags);
list_add(&file->f_u.fu_list, &delayed_fput_list); list_add(&file->f_u.fu_list, &delayed_fput_list);
schedule_work(&delayed_fput_work); schedule_work(&delayed_fput_work);
spin_unlock_irqrestore(&delayed_fput_lock, flags); spin_unlock_irqrestore(&delayed_fput_lock, flags);
return;
}
init_task_work(&file->f_u.fu_rcuhead, ____fput);
task_work_add(task, &file->f_u.fu_rcuhead, true);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment