Skip to content

Commit ca027af

Browse files
toddkjospundiramit
authored andcommitted
FROMLIST: binder: fix use-after-free in binder_transaction()
(from https://patchwork.kernel.org/patch/9978801/) 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, this can fail. In this case, when we attempt to decrement the node in the failure path, the node has already been freed. This is fixed by taking a tmpref on the node while constructing the transaction. To avoid re-acquiring the node lock and inner proc lock to increment the proc's tmpref, a helper is used that does the ref increments on both the node and proc. Bug: 66899329 Change-Id: Iad40e1e0bccee88234900494fb52a510a37fe8d7 Signed-off-by: Todd Kjos <tkjos@google.com>
1 parent d8e5929 commit ca027af

1 file changed

Lines changed: 66 additions & 27 deletions

File tree

drivers/android/binder.c

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2744,6 +2744,48 @@ static bool binder_proc_transaction(struct binder_transaction *t,
27442744
return true;
27452745
}
27462746

2747+
/**
2748+
* binder_get_node_refs_for_txn() - Get required refs on node for txn
2749+
* @node: struct binder_node for which to get refs
2750+
* @proc: returns @node->proc if valid
2751+
* @error: if no @proc then returns BR_DEAD_REPLY
2752+
*
2753+
* User-space normally keeps the node alive when creating a transaction
2754+
* since it has a reference to the target. The local strong ref keeps it
2755+
* alive if the sending process dies before the target process processes
2756+
* the transaction. If the source process is malicious or has a reference
2757+
* counting bug, relying on the local strong ref can fail.
2758+
*
2759+
* Since user-space can cause the local strong ref to go away, we also take
2760+
* a tmpref on the node to ensure it survives while we are constructing
2761+
* the transaction. We also need a tmpref on the proc while we are
2762+
* constructing the transaction, so we take that here as well.
2763+
*
2764+
* Return: The target_node with refs taken or NULL if no @node->proc is NULL.
2765+
* Also sets @proc if valid. If the @node->proc is NULL indicating that the
2766+
* target proc has died, @error is set to BR_DEAD_REPLY
2767+
*/
2768+
static struct binder_node *binder_get_node_refs_for_txn(
2769+
struct binder_node *node,
2770+
struct binder_proc **procp,
2771+
uint32_t *error)
2772+
{
2773+
struct binder_node *target_node = NULL;
2774+
2775+
binder_node_inner_lock(node);
2776+
if (node->proc) {
2777+
target_node = node;
2778+
binder_inc_node_nilocked(node, 1, 0, NULL);
2779+
binder_inc_node_tmpref_ilocked(node);
2780+
node->proc->tmp_ref++;
2781+
*procp = node->proc;
2782+
} else
2783+
*error = BR_DEAD_REPLY;
2784+
binder_node_inner_unlock(node);
2785+
2786+
return target_node;
2787+
}
2788+
27472789
static void binder_transaction(struct binder_proc *proc,
27482790
struct binder_thread *thread,
27492791
struct binder_transaction_data *tr, int reply,
@@ -2846,43 +2888,35 @@ static void binder_transaction(struct binder_proc *proc,
28462888
ref = binder_get_ref_olocked(proc, tr->target.handle,
28472889
true);
28482890
if (ref) {
2849-
binder_inc_node(ref->node, 1, 0, NULL);
2850-
target_node = ref->node;
2851-
}
2852-
binder_proc_unlock(proc);
2853-
if (target_node == NULL) {
2891+
target_node = binder_get_node_refs_for_txn(
2892+
ref->node, &target_proc,
2893+
&return_error);
2894+
} else {
28542895
binder_user_error("%d:%d got transaction to invalid handle\n",
2855-
proc->pid, thread->pid);
2896+
proc->pid, thread->pid);
28562897
return_error = BR_FAILED_REPLY;
2857-
return_error_param = -EINVAL;
2858-
return_error_line = __LINE__;
2859-
goto err_invalid_target_handle;
28602898
}
2899+
binder_proc_unlock(proc);
28612900
} else {
28622901
mutex_lock(&context->context_mgr_node_lock);
28632902
target_node = context->binder_context_mgr_node;
2864-
if (target_node == NULL) {
2903+
if (target_node)
2904+
target_node = binder_get_node_refs_for_txn(
2905+
target_node, &target_proc,
2906+
&return_error);
2907+
else
28652908
return_error = BR_DEAD_REPLY;
2866-
mutex_unlock(&context->context_mgr_node_lock);
2867-
return_error_line = __LINE__;
2868-
goto err_no_context_mgr_node;
2869-
}
2870-
binder_inc_node(target_node, 1, 0, NULL);
28712909
mutex_unlock(&context->context_mgr_node_lock);
28722910
}
2873-
e->to_node = target_node->debug_id;
2874-
binder_node_lock(target_node);
2875-
target_proc = target_node->proc;
2876-
if (target_proc == NULL) {
2877-
binder_node_unlock(target_node);
2878-
return_error = BR_DEAD_REPLY;
2911+
if (!target_node) {
2912+
/*
2913+
* return_error is set above
2914+
*/
2915+
return_error_param = -EINVAL;
28792916
return_error_line = __LINE__;
28802917
goto err_dead_binder;
28812918
}
2882-
binder_inner_proc_lock(target_proc);
2883-
target_proc->tmp_ref++;
2884-
binder_inner_proc_unlock(target_proc);
2885-
binder_node_unlock(target_node);
2919+
e->to_node = target_node->debug_id;
28862920
if (security_binder_transaction(proc->tsk,
28872921
target_proc->tsk) < 0) {
28882922
return_error = BR_FAILED_REPLY;
@@ -3241,6 +3275,8 @@ static void binder_transaction(struct binder_proc *proc,
32413275
if (target_thread)
32423276
binder_thread_dec_tmpref(target_thread);
32433277
binder_proc_dec_tmpref(target_proc);
3278+
if (target_node)
3279+
binder_dec_node_tmpref(target_node);
32443280
/*
32453281
* write barrier to synchronize with initialization
32463282
* of log entry
@@ -3260,6 +3296,8 @@ static void binder_transaction(struct binder_proc *proc,
32603296
err_copy_data_failed:
32613297
trace_binder_transaction_failed_buffer_release(t->buffer);
32623298
binder_transaction_buffer_release(target_proc, t->buffer, offp);
3299+
if (target_node)
3300+
binder_dec_node_tmpref(target_node);
32633301
target_node = NULL;
32643302
t->buffer->transaction = NULL;
32653303
binder_alloc_free_buf(&target_proc->alloc, t->buffer);
@@ -3274,13 +3312,14 @@ static void binder_transaction(struct binder_proc *proc,
32743312
err_empty_call_stack:
32753313
err_dead_binder:
32763314
err_invalid_target_handle:
3277-
err_no_context_mgr_node:
32783315
if (target_thread)
32793316
binder_thread_dec_tmpref(target_thread);
32803317
if (target_proc)
32813318
binder_proc_dec_tmpref(target_proc);
3282-
if (target_node)
3319+
if (target_node) {
32833320
binder_dec_node(target_node, 1, 0);
3321+
binder_dec_node_tmpref(target_node);
3322+
}
32843323

32853324
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
32863325
"%d:%d transaction failed %d/%d, size %lld-%lld line %d\n",

0 commit comments

Comments
 (0)