diff options
Diffstat (limited to 'drivers/android/binder.c')
| -rw-r--r-- | drivers/android/binder.c | 104 | 
1 files changed, 67 insertions, 37 deletions
| diff --git a/drivers/android/binder.c b/drivers/android/binder.c index ab34239a76ee..fddf76ef5bd6 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2582,6 +2582,48 @@ static bool binder_proc_transaction(struct binder_transaction *t,  	return true;  } +/** + * binder_get_node_refs_for_txn() - Get required refs on node for txn + * @node:         struct binder_node for which to get refs + * @proc:         returns @node->proc if valid + * @error:        if no @proc then returns BR_DEAD_REPLY + * + * User-space normally keeps the node alive when creating a transaction + * since it has a reference to the target. The local strong ref keeps it + * alive if the sending process dies before the target process processes + * the transaction. If the source process is malicious or has a reference + * counting bug, relying on the local strong ref can fail. + * + * Since user-space can cause the local strong ref to go away, we also take + * a tmpref on the node to ensure it survives while we are constructing + * the transaction. We also need a tmpref on the proc while we are + * constructing the transaction, so we take that here as well. + * + * Return: The target_node with refs taken or NULL if no @node->proc is NULL. + * Also sets @proc if valid. If the @node->proc is NULL indicating that the + * target proc has died, @error is set to BR_DEAD_REPLY + */ +static struct binder_node *binder_get_node_refs_for_txn( +		struct binder_node *node, +		struct binder_proc **procp, +		uint32_t *error) +{ +	struct binder_node *target_node = NULL; + +	binder_node_inner_lock(node); +	if (node->proc) { +		target_node = node; +		binder_inc_node_nilocked(node, 1, 0, NULL); +		binder_inc_node_tmpref_ilocked(node); +		node->proc->tmp_ref++; +		*procp = node->proc; +	} else +		*error = BR_DEAD_REPLY; +	binder_node_inner_unlock(node); + +	return target_node; +} +  static void binder_transaction(struct binder_proc *proc,  			       struct binder_thread *thread,  			       struct binder_transaction_data *tr, int reply, @@ -2685,43 +2727,35 @@ static void binder_transaction(struct binder_proc *proc,  			ref = binder_get_ref_olocked(proc, tr->target.handle,  						     true);  			if (ref) { -				binder_inc_node(ref->node, 1, 0, NULL); -				target_node = ref->node; -			} -			binder_proc_unlock(proc); -			if (target_node == NULL) { +				target_node = binder_get_node_refs_for_txn( +						ref->node, &target_proc, +						&return_error); +			} else {  				binder_user_error("%d:%d got transaction to invalid handle\n", -					proc->pid, thread->pid); +						  proc->pid, thread->pid);  				return_error = BR_FAILED_REPLY; -				return_error_param = -EINVAL; -				return_error_line = __LINE__; -				goto err_invalid_target_handle;  			} +			binder_proc_unlock(proc);  		} else {  			mutex_lock(&context->context_mgr_node_lock);  			target_node = context->binder_context_mgr_node; -			if (target_node == NULL) { +			if (target_node) +				target_node = binder_get_node_refs_for_txn( +						target_node, &target_proc, +						&return_error); +			else  				return_error = BR_DEAD_REPLY; -				mutex_unlock(&context->context_mgr_node_lock); -				return_error_line = __LINE__; -				goto err_no_context_mgr_node; -			} -			binder_inc_node(target_node, 1, 0, NULL);  			mutex_unlock(&context->context_mgr_node_lock);  		} -		e->to_node = target_node->debug_id; -		binder_node_lock(target_node); -		target_proc = target_node->proc; -		if (target_proc == NULL) { -			binder_node_unlock(target_node); -			return_error = BR_DEAD_REPLY; +		if (!target_node) { +			/* +			 * return_error is set above +			 */ +			return_error_param = -EINVAL;  			return_error_line = __LINE__;  			goto err_dead_binder;  		} -		binder_inner_proc_lock(target_proc); -		target_proc->tmp_ref++; -		binder_inner_proc_unlock(target_proc); -		binder_node_unlock(target_node); +		e->to_node = target_node->debug_id;  		if (security_binder_transaction(proc->tsk,  						target_proc->tsk) < 0) {  			return_error = BR_FAILED_REPLY; @@ -3071,6 +3105,8 @@ static void binder_transaction(struct binder_proc *proc,  	if (target_thread)  		binder_thread_dec_tmpref(target_thread);  	binder_proc_dec_tmpref(target_proc); +	if (target_node) +		binder_dec_node_tmpref(target_node);  	/*  	 * write barrier to synchronize with initialization  	 * of log entry @@ -3090,6 +3126,8 @@ err_bad_parent:  err_copy_data_failed:  	trace_binder_transaction_failed_buffer_release(t->buffer);  	binder_transaction_buffer_release(target_proc, t->buffer, offp); +	if (target_node) +		binder_dec_node_tmpref(target_node);  	target_node = NULL;  	t->buffer->transaction = NULL;  	binder_alloc_free_buf(&target_proc->alloc, t->buffer); @@ -3104,13 +3142,14 @@ err_bad_call_stack:  err_empty_call_stack:  err_dead_binder:  err_invalid_target_handle: -err_no_context_mgr_node:  	if (target_thread)  		binder_thread_dec_tmpref(target_thread);  	if (target_proc)  		binder_proc_dec_tmpref(target_proc); -	if (target_node) +	if (target_node) {  		binder_dec_node(target_node, 1, 0); +		binder_dec_node_tmpref(target_node); +	}  	binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,  		     "%d:%d transaction failed %d/%d, size %lld-%lld line %d\n", @@ -3623,12 +3662,6 @@ static void binder_stat_br(struct binder_proc *proc,  	}  } -static int binder_has_thread_work(struct binder_thread *thread) -{ -	return !binder_worklist_empty(thread->proc, &thread->todo) || -		thread->looper_need_return; -} -  static int binder_put_node_cmd(struct binder_proc *proc,  			       struct binder_thread *thread,  			       void __user **ptrp, @@ -4258,12 +4291,9 @@ static unsigned int binder_poll(struct file *filp,  	binder_inner_proc_unlock(thread->proc); -	if (binder_has_work(thread, wait_for_proc_work)) -		return POLLIN; -  	poll_wait(filp, &thread->wait, wait); -	if (binder_has_thread_work(thread)) +	if (binder_has_work(thread, wait_for_proc_work))  		return POLLIN;  	return 0; | 
