Staging: binder: Keep a reference to the files_struct while the driver is mmapped

This prevents breaking fget_light if a single threaded application
allows incoming file descriptors (in replies or on nodes).
Should also prevent inserting a file in the wrong files_struct if the
receving process execs in the middle of a transaction (between
task_get_unused_fd_flags and task_fd_install).

Signed-off-by: Arve Hjønnevåg <arve@android.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c
index 64f95ee..09962e8 100644
--- a/drivers/staging/android/binder.c
+++ b/drivers/staging/android/binder.c
@@ -41,6 +41,8 @@
 static struct proc_dir_entry *binder_proc_dir_entry_root;
 static struct proc_dir_entry *binder_proc_dir_entry_proc;
 static struct hlist_head binder_dead_nodes;
+static HLIST_HEAD(binder_release_files_list);
+static DEFINE_MUTEX(binder_release_files_lock);
 
 static int binder_read_proc_proc(
 	char *page, char **start, off_t off, int count, int *eof, void *data);
@@ -241,6 +243,8 @@
 	int pid;
 	struct vm_area_struct *vma;
 	struct task_struct *tsk;
+	struct files_struct *files;
+	struct hlist_node release_files_node;
 	void *buffer;
 	size_t user_buffer_offset;
 
@@ -309,9 +313,9 @@
 /*
  * copied from get_unused_fd_flags
  */
-int task_get_unused_fd_flags(struct task_struct *tsk, int flags)
+int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
 {
-	struct files_struct *files = get_files_struct(tsk);
+	struct files_struct *files = proc->files;
 	int fd, error;
 	struct fdtable *fdt;
 	unsigned long rlim_cur;
@@ -333,9 +337,9 @@
 	 * will limit the total number of files that can be opened.
 	 */
 	rlim_cur = 0;
-	if (lock_task_sighand(tsk, &irqs)) {
-		rlim_cur = tsk->signal->rlim[RLIMIT_NOFILE].rlim_cur;
-		unlock_task_sighand(tsk, &irqs);
+	if (lock_task_sighand(proc->tsk, &irqs)) {
+		rlim_cur = proc->tsk->signal->rlim[RLIMIT_NOFILE].rlim_cur;
+		unlock_task_sighand(proc->tsk, &irqs);
 	}
 	if (fd >= rlim_cur)
 		goto out;
@@ -371,7 +375,6 @@
 
 out:
 	spin_unlock(&files->file_lock);
-	put_files_struct(files);
 	return error;
 }
 
@@ -379,9 +382,9 @@
  * copied from fd_install
  */
 static void task_fd_install(
-	struct task_struct *tsk, unsigned int fd, struct file *file)
+	struct binder_proc *proc, unsigned int fd, struct file *file)
 {
-	struct files_struct *files = get_files_struct(tsk);
+	struct files_struct *files = proc->files;
 	struct fdtable *fdt;
 
 	if (files == NULL)
@@ -392,7 +395,6 @@
 	BUG_ON(fdt->fd[fd] != NULL);
 	rcu_assign_pointer(fdt->fd[fd], file);
 	spin_unlock(&files->file_lock);
-	put_files_struct(files);
 }
 
 /*
@@ -409,10 +411,10 @@
 /*
  * copied from sys_close
  */
-static long task_close_fd(struct task_struct *tsk, unsigned int fd)
+static long task_close_fd(struct binder_proc *proc, unsigned int fd)
 {
 	struct file *filp;
-	struct files_struct *files = get_files_struct(tsk);
+	struct files_struct *files = proc->files;
 	struct fdtable *fdt;
 	int retval;
 
@@ -439,12 +441,10 @@
 		     retval == -ERESTART_RESTARTBLOCK))
 		retval = -EINTR;
 
-	put_files_struct(files);
 	return retval;
 
 out_unlock:
 	spin_unlock(&files->file_lock);
-	put_files_struct(files);
 	return -EBADF;
 }
 
@@ -1549,13 +1549,13 @@
 				return_error = BR_FAILED_REPLY;
 				goto err_fget_failed;
 			}
-			target_fd = task_get_unused_fd_flags(target_proc->tsk, O_CLOEXEC);
+			target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
 			if (target_fd < 0) {
 				fput(file);
 				return_error = BR_FAILED_REPLY;
 				goto err_get_unused_fd_failed;
 			}
-			task_fd_install(target_proc->tsk, target_fd, file);
+			task_fd_install(target_proc, target_fd, file);
 			if (binder_debug_mask & BINDER_DEBUG_TRANSACTION)
 				printk(KERN_INFO "        fd %ld -> %d\n", fp->handle, target_fd);
 			/* TODO: fput? */
@@ -1698,7 +1698,7 @@
 			if (binder_debug_mask & BINDER_DEBUG_TRANSACTION)
 				printk(KERN_INFO "        fd %ld\n", fp->handle);
 			if (failed_at)
-				task_close_fd(proc->tsk, fp->handle);
+				task_close_fd(proc, fp->handle);
 			break;
 
 		default:
@@ -2663,6 +2663,34 @@
 			(unsigned long)pgprot_val(vma->vm_page_prot));
 	dump_stack();
 }
+
+static void binder_release_files(struct work_struct *work)
+{
+	struct binder_proc *proc;
+	struct files_struct *files;
+	do {
+		mutex_lock(&binder_lock);
+		mutex_lock(&binder_release_files_lock);
+		if (!hlist_empty(&binder_release_files_list)) {
+			proc = hlist_entry(binder_release_files_list.first,
+					struct binder_proc, release_files_node);
+			hlist_del_init(&proc->release_files_node);
+			files = proc->files;
+			if (files)
+				proc->files = NULL;
+		} else {
+			proc = NULL;
+			files = NULL;
+		}
+		mutex_unlock(&binder_release_files_lock);
+		mutex_unlock(&binder_lock);
+		if (files)
+			put_files_struct(files);
+	} while (proc);
+}
+
+static DECLARE_WORK(binder_release_files_work, binder_release_files);
+
 static void binder_vma_close(struct vm_area_struct *vma)
 {
 	struct binder_proc *proc = vma->vm_private_data;
@@ -2673,6 +2701,13 @@
 			(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
 			(unsigned long)pgprot_val(vma->vm_page_prot));
 	proc->vma = NULL;
+	mutex_lock(&binder_release_files_lock);
+	if (proc->files) {
+		hlist_add_head(&proc->release_files_node,
+				&binder_release_files_list);
+		schedule_work(&binder_release_files_work);
+	}
+	mutex_unlock(&binder_release_files_lock);
 }
 
 static struct vm_operations_struct binder_vm_ops = {
@@ -2751,6 +2786,7 @@
 	binder_insert_free_buffer(proc, buffer);
 	proc->free_async_space = proc->buffer_size / 2;
 	barrier();
+	proc->files = get_files_struct(current);
 	proc->vma = vma;
 
 	/*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
@@ -2831,6 +2867,7 @@
 	struct hlist_node *pos;
 	struct binder_transaction *t;
 	struct rb_node *n;
+	struct files_struct *files;
 	struct binder_proc *proc = filp->private_data;
 	int threads, nodes, incoming_refs, outgoing_refs, buffers, active_transactions, page_count;
 
@@ -2840,6 +2877,14 @@
 		remove_proc_entry(strbuf, binder_proc_dir_entry_proc);
 	}
 	mutex_lock(&binder_lock);
+	mutex_lock(&binder_release_files_lock);
+	if (!hlist_unhashed(&proc->release_files_node))
+		hlist_del(&proc->release_files_node);
+	files = proc->files;
+	if (files)
+		proc->files = NULL;
+	mutex_unlock(&binder_release_files_lock);
+
 	hlist_del(&proc->proc_node);
 	if (binder_context_mgr_node && binder_context_mgr_node->proc == proc) {
 		if (binder_debug_mask & BINDER_DEBUG_DEAD_BINDER)
@@ -2937,6 +2982,8 @@
 		       proc->pid, threads, nodes, incoming_refs, outgoing_refs, active_transactions, buffers, page_count);
 
 	kfree(proc);
+	if (files)
+		put_files_struct(files);
 	return 0;
 }