Skip to content

Commit 44b7396

Browse files
Sherry Yanggregkh
authored andcommitted
android: binder: no outgoing transaction when thread todo has transaction
When a process dies, failed reply is sent to the sender of any transaction queued on a dead thread's todo list. The sender asserts that the received failed reply corresponds to the head of the transaction stack. This assert can fail if the dead thread is allowed to send outgoing transactions when there is already a transaction on its todo list, because this new transaction can end up on the transaction stack of the original sender. The following steps illustrate how this assertion can fail. 1. Thread1 sends txn19 to Thread2 (T1->transaction_stack=txn19, T2->todo+=txn19) 2. Without processing todo list, Thread2 sends txn20 to Thread1 (T1->todo+=txn20, T2->transaction_stack=txn20) 3. T1 processes txn20 on its todo list (T1->transaction_stack=txn20->txn19, T1->todo=<empty>) 4. T2 dies, T2->todo cleanup attempts to send failed reply for txn19, but T1->transaction_stack points to txn20 -- assertion failes Step 2. is the incorrect behavior. When there is a transaction on a thread's todo list, this thread should not be able to send any outgoing synchronous transactions. Only the head of the todo list needs to be checked because only threads that are waiting for proc work can directly receive work from another thread, and no work is allowed to be queued on such a thread without waking up the thread. This patch also enforces that a thread is not waiting for proc work when a work is directly enqueued to its todo list. Acked-by: Arve Hjønnevåg <[email protected]> Signed-off-by: Sherry Yang <[email protected]> Reviewed-by: Martijn Coenen <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 79c6f4b commit 44b7396

File tree

1 file changed

+32
-12
lines changed

1 file changed

+32
-12
lines changed

drivers/android/binder.c

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,7 @@ static void
822822
binder_enqueue_deferred_thread_work_ilocked(struct binder_thread *thread,
823823
struct binder_work *work)
824824
{
825+
WARN_ON(!list_empty(&thread->waiting_thread_node));
825826
binder_enqueue_work_ilocked(work, &thread->todo);
826827
}
827828

@@ -839,6 +840,7 @@ static void
839840
binder_enqueue_thread_work_ilocked(struct binder_thread *thread,
840841
struct binder_work *work)
841842
{
843+
WARN_ON(!list_empty(&thread->waiting_thread_node));
842844
binder_enqueue_work_ilocked(work, &thread->todo);
843845
thread->process_todo = true;
844846
}
@@ -1270,19 +1272,12 @@ static int binder_inc_node_nilocked(struct binder_node *node, int strong,
12701272
} else
12711273
node->local_strong_refs++;
12721274
if (!node->has_strong_ref && target_list) {
1275+
struct binder_thread *thread = container_of(target_list,
1276+
struct binder_thread, todo);
12731277
binder_dequeue_work_ilocked(&node->work);
1274-
/*
1275-
* Note: this function is the only place where we queue
1276-
* directly to a thread->todo without using the
1277-
* corresponding binder_enqueue_thread_work() helper
1278-
* functions; in this case it's ok to not set the
1279-
* process_todo flag, since we know this node work will
1280-
* always be followed by other work that starts queue
1281-
* processing: in case of synchronous transactions, a
1282-
* BR_REPLY or BR_ERROR; in case of oneway
1283-
* transactions, a BR_TRANSACTION_COMPLETE.
1284-
*/
1285-
binder_enqueue_work_ilocked(&node->work, target_list);
1278+
BUG_ON(&thread->todo != target_list);
1279+
binder_enqueue_deferred_thread_work_ilocked(thread,
1280+
&node->work);
12861281
}
12871282
} else {
12881283
if (!internal)
@@ -2723,6 +2718,7 @@ static void binder_transaction(struct binder_proc *proc,
27232718
{
27242719
int ret;
27252720
struct binder_transaction *t;
2721+
struct binder_work *w;
27262722
struct binder_work *tcomplete;
27272723
binder_size_t *offp, *off_end, *off_start;
27282724
binder_size_t off_min;
@@ -2864,6 +2860,29 @@ static void binder_transaction(struct binder_proc *proc,
28642860
goto err_invalid_target_handle;
28652861
}
28662862
binder_inner_proc_lock(proc);
2863+
2864+
w = list_first_entry_or_null(&thread->todo,
2865+
struct binder_work, entry);
2866+
if (!(tr->flags & TF_ONE_WAY) && w &&
2867+
w->type == BINDER_WORK_TRANSACTION) {
2868+
/*
2869+
* Do not allow new outgoing transaction from a
2870+
* thread that has a transaction at the head of
2871+
* its todo list. Only need to check the head
2872+
* because binder_select_thread_ilocked picks a
2873+
* thread from proc->waiting_threads to enqueue
2874+
* the transaction, and nothing is queued to the
2875+
* todo list while the thread is on waiting_threads.
2876+
*/
2877+
binder_user_error("%d:%d new transaction not allowed when there is a transaction on thread todo\n",
2878+
proc->pid, thread->pid);
2879+
binder_inner_proc_unlock(proc);
2880+
return_error = BR_FAILED_REPLY;
2881+
return_error_param = -EPROTO;
2882+
return_error_line = __LINE__;
2883+
goto err_bad_todo_list;
2884+
}
2885+
28672886
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
28682887
struct binder_transaction *tmp;
28692888

@@ -3247,6 +3266,7 @@ static void binder_transaction(struct binder_proc *proc,
32473266
kfree(t);
32483267
binder_stats_deleted(BINDER_STAT_TRANSACTION);
32493268
err_alloc_t_failed:
3269+
err_bad_todo_list:
32503270
err_bad_call_stack:
32513271
err_empty_call_stack:
32523272
err_dead_binder:

0 commit comments

Comments
 (0)