Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aefd2d63 authored by Todd Kjos's avatar Todd Kjos
Browse files

ANDROID: binder: fix sleeping from invalid function caused by RT inheritance



When changing a thread's scheduling priority, binder calls
sched_setscheduler_nocheck() while holding the node lock and
proc inner lock. This was safe until v5.3 when a change was
introduced where cpuset_read_lock() is called in this path
which can sleep: commit 710da3c8 ("sched/core: Prevent
race condition between cpuset and __sched_setscheduler()").

Refactored binder_proc_transaction() to avoid holding a lock
when calling binder_transaction_priority().

Bug: 143627611
Change-Id: I405c76b4813777905090ccc33e4f048b37700068
Fixes: e00eb41c ("ANDROID: binder: add support for RT prio inheritance.")
Signed-off-by: default avatarTodd Kjos <tkjos@google.com>
parent a30de0e0
Loading
Loading
Loading
Loading
+46 −9
Original line number Diff line number Diff line
@@ -2911,19 +2911,50 @@ static bool binder_proc_transaction(struct binder_transaction *t,
	struct binder_priority node_prio;
	bool oneway = !!(t->flags & TF_ONE_WAY);
	bool pending_async = false;
	bool retry = false;

	BUG_ON(!node);
	binder_node_lock(node);

set_thread_prio:
	node_prio.prio = node->min_priority;
	node_prio.sched_policy = node->sched_policy;
	if (thread) {
		/*
		 * Priority must be set outside of lock, but must be
		 * done before enqueuing the transaction.
		 */
		binder_transaction_priority(thread->task, t, node_prio,
					    node->inherit_rt);
	}

retry_after_prio_restore:
	binder_node_lock(node);

	if (oneway) {
		BUG_ON(thread);
		BUG_ON(!retry && thread);
		if (node->has_async_transaction) {
			pending_async = true;
		} else {
			node->has_async_transaction = true;
		}
		if (thread && pending_async) {
			/*
			 * The node state has changed since we selected
			 * the thread. Return the thread to the
			 * waiting_threads list. We have to drop
			 * the node lock to restore priority so we
			 * have to re-check the node state.
			 */
			binder_node_unlock(node);
			binder_restore_priority(thread->task,
						proc->default_priority);
			binder_inner_proc_lock(proc);
			list_add(&thread->waiting_thread_node,
				 &proc->waiting_threads);
			binder_inner_proc_unlock(proc);
			thread = NULL;
			goto retry_after_prio_restore;
		}
	}

	binder_inner_proc_lock(proc);
@@ -2934,18 +2965,24 @@ static bool binder_proc_transaction(struct binder_transaction *t,
		return false;
	}

	if (!thread && !pending_async)
	if (!thread && !pending_async) {
		thread = binder_select_thread_ilocked(proc);

		if (thread) {
		binder_transaction_priority(thread->task, t, node_prio,
					    node->inherit_rt);
			if (oneway)
				node->has_async_transaction = false;
			binder_inner_proc_unlock(proc);
			binder_node_unlock(node);
			retry = true;
			goto set_thread_prio;
		}
	}

	if (thread)
		binder_enqueue_thread_work_ilocked(thread, &t->work);
	} else if (!pending_async) {
	else if (!pending_async)
		binder_enqueue_work_ilocked(&t->work, &proc->todo);
	} else {
	else
		binder_enqueue_work_ilocked(&t->work, &node->async_todo);
	}

	if (!pending_async)
		binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);