ANDROID: binder: don't queue async transactions to thread.
authorMartijn Coenen <maco@android.com>
Thu, 10 Aug 2017 11:56:16 +0000 (13:56 +0200)
committerDanny Wood <danwood76@gmail.com>
Tue, 26 Feb 2019 16:35:31 +0000 (16:35 +0000)
This can cause issues with processes using the poll()
interface:

1) client sends two oneway transactions
2) the second one gets queued on async_todo
   (because the server didn't handle the first one
    yet)
3) server returns from poll(), picks up the
   first transaction and does transaction work
4) server is done with the transaction, sends
   BC_FREE_BUFFER, and the second transaction gets
   moved to thread->todo
5) libbinder's handlePolledCommands() only handles
   the commands in the current data buffer, so
   doesn't see the new transaction
6) the server continues running and issues a new
   outgoing transaction. Now, it suddenly finds
   the incoming oneway transaction on its thread
   todo, and returns that to userspace.
7) userspace does not expect this to happen; it
   may be holding a lock while making the outgoing
   transaction, and if handling the incoming
   trasnaction requires taking the same lock,
   userspace will deadlock.

By queueing the async transaction to the proc
workqueue, we make sure it's only picked up when
a thread is ready for proc work.

Bug: 38201220
Bug: 63075553
Bug: 63079216

Change-Id: I84268cc112f735d7e3173793873dfdb4b268468b
Signed-off-by: Martijn Coenen <maco@android.com>
drivers/android/binder.c

index c3968322d9fc5152c8ac27b418a076264b680658..a0e7d983736f42e352ac4c2a1d38c9d4702e3cfa 100644 (file)
@@ -3529,11 +3529,13 @@ int binder_thread_write(struct binder_proc *proc,
                                BUG_ON(buf_node->proc != proc);
                                w = binder_dequeue_work_head_ilocked(
                                                &buf_node->async_todo);
-                               if (!w)
+                               if (!w) {
                                        buf_node->has_async_transaction = 0;
-                               else
+                               } else {
                                        binder_enqueue_work_ilocked(
-                                                       w, &thread->todo);
+                                                       w, &proc->todo);
+                                       binder_wakeup_proc_ilocked(proc);
+                               }
                                binder_node_inner_unlock(buf_node);
                        }
                        trace_binder_transaction_buffer_release(buffer);