binder: fix possible race with put_files_struct

It hasn't been observed, but it is possible for put_files_struct()
to be called while a transaction is in progress and calling
__alloc_fd() using the same files_struct. Use the zombie deferred
free mechanism to guarantee that no threads can refer to the
old proc->files when put_files_struct() is called.

Bug: 32225111
Test: tested manually
Change-Id: I5ef04b642e9135e19586e0e526c3869fb08a4bad
This commit is contained in:
Todd Kjos
2017-02-02 10:20:37 -08:00
committed by Thierry Strudel
parent fade544640
commit 6956e166c1

View File

@@ -366,6 +366,7 @@ struct binder_proc {
int active_thread_count;
struct task_struct *tsk;
struct files_struct *files;
struct files_struct *zombie_files;
struct hlist_node deferred_work_node;
int deferred_work;
spinlock_t proc_lock;
@@ -4029,6 +4030,8 @@ static bool binder_proc_clear_zombies(struct binder_proc *proc)
struct hlist_head threads_to_free;
struct hlist_head refs_to_free;
bool needs_requeue = false;
struct files_struct *files;
INIT_HLIST_HEAD(&nodes_to_free);
INIT_HLIST_HEAD(&threads_to_free);
INIT_HLIST_HEAD(&refs_to_free);
@@ -4062,8 +4065,14 @@ static bool binder_proc_clear_zombies(struct binder_proc *proc)
if (!RB_EMPTY_ROOT(&proc->threads))
needs_requeue = true;
files = proc->zombie_files;
proc->zombie_files = NULL;
binder_proc_unlock(proc, __LINE__);
if (files)
put_files_struct(files);
hlist_for_each_entry_safe(node, tmp, &nodes_to_free, dead_node) {
hlist_del_init(&node->dead_node);
binder_dequeue_work(&node->work, __LINE__);
@@ -4148,7 +4157,6 @@ static void binder_clear_zombies(void)
static void binder_deferred_func(struct work_struct *work)
{
struct binder_proc *proc;
struct files_struct *files;
int defer;
do {
@@ -4166,11 +4174,15 @@ static void binder_deferred_func(struct work_struct *work)
}
mutex_unlock(&binder_deferred_lock);
files = NULL;
if (defer & BINDER_DEFERRED_PUT_FILES) {
files = proc->files;
if (files)
binder_proc_lock(proc, __LINE__);
if (proc->files) {
BUG_ON(proc->zombie_files);
proc->zombie_files = proc->files;
proc->files = NULL;
binder_queue_for_zombie_cleanup(proc);
}
binder_proc_unlock(proc, __LINE__);
}
if (defer & BINDER_DEFERRED_FLUSH)
@@ -4183,8 +4195,6 @@ static void binder_deferred_func(struct work_struct *work)
binder_clear_zombies();
binder_unlock(__func__);
if (files)
put_files_struct(files);
} while (proc);
}