diff options
Diffstat (limited to 'drivers/android')
| -rw-r--r-- | drivers/android/binder.c | 84 | 
1 files changed, 62 insertions, 22 deletions
diff --git a/drivers/android/binder.c b/drivers/android/binder.c index a73596a4f804..a7ecfde66b7b 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -482,7 +482,8 @@ enum binder_deferred_state {   * @tsk                   task_struct for group_leader of process   *                        (invariant after initialized)   * @files                 files_struct for process - *                        (invariant after initialized) + *                        (protected by @files_lock) + * @files_lock            mutex to protect @files   * @deferred_work_node:   element for binder_deferred_list   *                        (protected by binder_deferred_lock)   * @deferred_work:        bitmap of deferred work to perform @@ -530,6 +531,7 @@ struct binder_proc {  	int pid;  	struct task_struct *tsk;  	struct files_struct *files; +	struct mutex files_lock;  	struct hlist_node deferred_work_node;  	int deferred_work;  	bool is_dead; @@ -877,20 +879,26 @@ static void binder_inc_node_tmpref_ilocked(struct binder_node *node);  static int task_get_unused_fd_flags(struct binder_proc *proc, int flags)  { -	struct files_struct *files = proc->files;  	unsigned long rlim_cur;  	unsigned long irqs; +	int ret; -	if (files == NULL) -		return -ESRCH; - -	if (!lock_task_sighand(proc->tsk, &irqs)) -		return -EMFILE; - +	mutex_lock(&proc->files_lock); +	if (proc->files == NULL) { +		ret = -ESRCH; +		goto err; +	} +	if (!lock_task_sighand(proc->tsk, &irqs)) { +		ret = -EMFILE; +		goto err; +	}  	rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE);  	unlock_task_sighand(proc->tsk, &irqs); -	return __alloc_fd(files, 0, rlim_cur, flags); +	ret = __alloc_fd(proc->files, 0, rlim_cur, flags); +err: +	mutex_unlock(&proc->files_lock); +	return ret;  }  /* @@ -899,8 +907,10 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags)  static void task_fd_install(  	struct binder_proc *proc, unsigned int fd, struct file *file)  { +	mutex_lock(&proc->files_lock);  	if (proc->files)  		__fd_install(proc->files, fd, file); +	mutex_unlock(&proc->files_lock);  }  /* @@ -910,9 +920,11 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd)  {  	int retval; -	if (proc->files == NULL) -		return -ESRCH; - +	mutex_lock(&proc->files_lock); +	if (proc->files == NULL) { +		retval = -ESRCH; +		goto err; +	}  	retval = __close_fd(proc->files, fd);  	/* can't restart close syscall because file table entry was cleared */  	if (unlikely(retval == -ERESTARTSYS || @@ -920,7 +932,8 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd)  		     retval == -ERESTARTNOHAND ||  		     retval == -ERESTART_RESTARTBLOCK))  		retval = -EINTR; - +err: +	mutex_unlock(&proc->files_lock);  	return retval;  } @@ -1948,6 +1961,26 @@ static void binder_send_failed_reply(struct binder_transaction *t,  }  /** + * binder_cleanup_transaction() - cleans up undelivered transaction + * @t:		transaction that needs to be cleaned up + * @reason:	reason the transaction wasn't delivered + * @error_code:	error to return to caller (if synchronous call) + */ +static void binder_cleanup_transaction(struct binder_transaction *t, +				       const char *reason, +				       uint32_t error_code) +{ +	if (t->buffer->target_node && !(t->flags & TF_ONE_WAY)) { +		binder_send_failed_reply(t, error_code); +	} else { +		binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, +			"undelivered transaction %d, %s\n", +			t->debug_id, reason); +		binder_free_transaction(t); +	} +} + +/**   * binder_validate_object() - checks for a valid metadata object in a buffer.   * @buffer:	binder_buffer that we're parsing.   * @offset:	offset in the buffer at which to validate an object. @@ -4015,12 +4048,20 @@ retry:  		if (put_user(cmd, (uint32_t __user *)ptr)) {  			if (t_from)  				binder_thread_dec_tmpref(t_from); + +			binder_cleanup_transaction(t, "put_user failed", +						   BR_FAILED_REPLY); +  			return -EFAULT;  		}  		ptr += sizeof(uint32_t);  		if (copy_to_user(ptr, &tr, sizeof(tr))) {  			if (t_from)  				binder_thread_dec_tmpref(t_from); + +			binder_cleanup_transaction(t, "copy_to_user failed", +						   BR_FAILED_REPLY); +  			return -EFAULT;  		}  		ptr += sizeof(tr); @@ -4090,15 +4131,9 @@ static void binder_release_work(struct binder_proc *proc,  			struct binder_transaction *t;  			t = container_of(w, struct binder_transaction, work); -			if (t->buffer->target_node && -			    !(t->flags & TF_ONE_WAY)) { -				binder_send_failed_reply(t, BR_DEAD_REPLY); -			} else { -				binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, -					"undelivered transaction %d\n", -					t->debug_id); -				binder_free_transaction(t); -			} + +			binder_cleanup_transaction(t, "process died.", +						   BR_DEAD_REPLY);  		} break;  		case BINDER_WORK_RETURN_ERROR: {  			struct binder_error *e = container_of( @@ -4605,7 +4640,9 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)  	ret = binder_alloc_mmap_handler(&proc->alloc, vma);  	if (ret)  		return ret; +	mutex_lock(&proc->files_lock);  	proc->files = get_files_struct(current); +	mutex_unlock(&proc->files_lock);  	return 0;  err_bad_arg: @@ -4629,6 +4666,7 @@ static int binder_open(struct inode *nodp, struct file *filp)  	spin_lock_init(&proc->outer_lock);  	get_task_struct(current->group_leader);  	proc->tsk = current->group_leader; +	mutex_init(&proc->files_lock);  	INIT_LIST_HEAD(&proc->todo);  	proc->default_priority = task_nice(current);  	binder_dev = container_of(filp->private_data, struct binder_device, @@ -4881,9 +4919,11 @@ static void binder_deferred_func(struct work_struct *work)  		files = NULL;  		if (defer & BINDER_DEFERRED_PUT_FILES) { +			mutex_lock(&proc->files_lock);  			files = proc->files;  			if (files)  				proc->files = NULL; +			mutex_unlock(&proc->files_lock);  		}  		if (defer & BINDER_DEFERRED_FLUSH)  | 

