diff options
Diffstat (limited to 'drivers/android')
| -rw-r--r-- | drivers/android/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/android/binder.c | 112 | ||||
| -rw-r--r-- | drivers/android/binder_alloc.c | 42 | ||||
| -rw-r--r-- | drivers/android/binder_alloc.h | 1 | 
4 files changed, 94 insertions, 62 deletions
diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index 9801d852bd56..7dce3795b887 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0  menu "Android"  config ANDROID diff --git a/drivers/android/binder.c b/drivers/android/binder.c index d055b3f2a207..fddf76ef5bd6 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2217,7 +2217,7 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,  				       debug_id, (u64)fda->num_fds);  				continue;  			} -			fd_array = (u32 *)(parent_buffer + fda->parent_offset); +			fd_array = (u32 *)(parent_buffer + (uintptr_t)fda->parent_offset);  			for (fd_index = 0; fd_index < fda->num_fds; fd_index++)  				task_close_fd(proc, fd_array[fd_index]);  		} break; @@ -2326,7 +2326,6 @@ static int binder_translate_handle(struct flat_binder_object *fp,  			     (u64)node->ptr);  		binder_node_unlock(node);  	} else { -		int ret;  		struct binder_ref_data dest_rdata;  		binder_node_unlock(node); @@ -2442,7 +2441,7 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda,  	 */  	parent_buffer = parent->buffer -  		binder_alloc_get_user_buffer_offset(&target_proc->alloc); -	fd_array = (u32 *)(parent_buffer + fda->parent_offset); +	fd_array = (u32 *)(parent_buffer + (uintptr_t)fda->parent_offset);  	if (!IS_ALIGNED((unsigned long)fd_array, sizeof(u32))) {  		binder_user_error("%d:%d parent offset not aligned correctly.\n",  				  proc->pid, thread->pid); @@ -2508,7 +2507,7 @@ static int binder_fixup_parent(struct binder_transaction *t,  				  proc->pid, thread->pid);  		return -EINVAL;  	} -	parent_buffer = (u8 *)(parent->buffer - +	parent_buffer = (u8 *)((uintptr_t)parent->buffer -  			binder_alloc_get_user_buffer_offset(  				&target_proc->alloc));  	*(binder_uintptr_t *)(parent_buffer + bp->parent_offset) = bp->buffer; @@ -2583,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, @@ -2686,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; @@ -3072,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 @@ -3083,6 +3118,7 @@ static void binder_transaction(struct binder_proc *proc,  err_dead_proc_or_thread:  	return_error = BR_DEAD_REPLY;  	return_error_line = __LINE__; +	binder_dequeue_work(proc, tcomplete);  err_translate_failed:  err_bad_object_type:  err_bad_offset: @@ -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; diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 8fe165844e47..c2819a3d58a6 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -215,17 +215,12 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,  		}  	} -	if (!vma && need_mm) -		mm = get_task_mm(alloc->tsk); +	if (!vma && need_mm && mmget_not_zero(alloc->vma_vm_mm)) +		mm = alloc->vma_vm_mm;  	if (mm) {  		down_write(&mm->mmap_sem);  		vma = alloc->vma; -		if (vma && mm != alloc->vma_vm_mm) { -			pr_err("%d: vma mm and task mm mismatch\n", -				alloc->pid); -			vma = NULL; -		}  	}  	if (!vma && need_mm) { @@ -565,7 +560,7 @@ static void binder_delete_free_buffer(struct binder_alloc *alloc,  		binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,  				   "%d: merge free, buffer %pK do not share page with %pK or %pK\n",  				   alloc->pid, buffer->data, -				   prev->data, next->data); +				   prev->data, next ? next->data : NULL);  		binder_update_page_range(alloc, 0, buffer_start_page(buffer),  					 buffer_start_page(buffer) + PAGE_SIZE,  					 NULL); @@ -720,6 +715,7 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,  	barrier();  	alloc->vma = vma;  	alloc->vma_vm_mm = vma->vm_mm; +	mmgrab(alloc->vma_vm_mm);  	return 0; @@ -795,6 +791,8 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)  		vfree(alloc->buffer);  	}  	mutex_unlock(&alloc->mutex); +	if (alloc->vma_vm_mm) +		mmdrop(alloc->vma_vm_mm);  	binder_alloc_debug(BINDER_DEBUG_OPEN_CLOSE,  		     "%s: %d buffers %d, pages %d\n", @@ -889,7 +887,6 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc)  void binder_alloc_vma_close(struct binder_alloc *alloc)  {  	WRITE_ONCE(alloc->vma, NULL); -	WRITE_ONCE(alloc->vma_vm_mm, NULL);  }  /** @@ -913,6 +910,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item,  	struct binder_alloc *alloc;  	uintptr_t page_addr;  	size_t index; +	struct vm_area_struct *vma;  	alloc = page->alloc;  	if (!mutex_trylock(&alloc->mutex)) @@ -923,16 +921,22 @@ enum lru_status binder_alloc_free_page(struct list_head *item,  	index = page - alloc->pages;  	page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE; -	if (alloc->vma) { -		mm = get_task_mm(alloc->tsk); -		if (!mm) -			goto err_get_task_mm_failed; +	vma = alloc->vma; +	if (vma) { +		if (!mmget_not_zero(alloc->vma_vm_mm)) +			goto err_mmget; +		mm = alloc->vma_vm_mm;  		if (!down_write_trylock(&mm->mmap_sem))  			goto err_down_write_mmap_sem_failed; +	} + +	list_lru_isolate(lru, item); +	spin_unlock(lock); +	if (vma) {  		trace_binder_unmap_user_start(alloc, index); -		zap_page_range(alloc->vma, +		zap_page_range(vma,  			       page_addr + alloc->user_buffer_offset,  			       PAGE_SIZE); @@ -950,14 +954,13 @@ enum lru_status binder_alloc_free_page(struct list_head *item,  	trace_binder_unmap_kernel_end(alloc, index); -	list_lru_isolate(lru, item); - +	spin_lock(lock);  	mutex_unlock(&alloc->mutex); -	return LRU_REMOVED; +	return LRU_REMOVED_RETRY;  err_down_write_mmap_sem_failed: -	mmput(mm); -err_get_task_mm_failed: +	mmput_async(mm); +err_mmget:  err_page_already_freed:  	mutex_unlock(&alloc->mutex);  err_get_alloc_mutex_failed: @@ -996,7 +999,6 @@ struct shrinker binder_shrinker = {   */  void binder_alloc_init(struct binder_alloc *alloc)  { -	alloc->tsk = current->group_leader;  	alloc->pid = current->group_leader->pid;  	mutex_init(&alloc->mutex);  	INIT_LIST_HEAD(&alloc->buffers); diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index a3a3602c689c..2dd33b6df104 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -100,7 +100,6 @@ struct binder_lru_page {   */  struct binder_alloc {  	struct mutex mutex; -	struct task_struct *tsk;  	struct vm_area_struct *vma;  	struct mm_struct *vma_vm_mm;  	void *buffer;  |