binder: freeze multiple contexts
binder freeze stops at the first context found for any pid, but
multiple ones are possible with the result that a process might end
up with inconsistent context states after freezing or unfreezing its
binder.
Freeze or unfreeze all contexts in a process upon a BINDER_FREEZE
ioctl.
Bug: 176996063
Test: verified that all contexts in a specific process with multiple
binders are frozen or unfrozen.
Signed-off-by: Marco Ballesio <balejs@google.com>
Change-Id: If0822e078e830e9fde10cc17b99e39ec7cf358d5
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 9a5cbf0..6d3de01 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -4987,20 +4987,66 @@ static int binder_ioctl_get_node_debug_info(struct binder_proc *proc,
return 0;
}
+static int binder_ioctl_freeze(struct binder_freeze_info *info,
+ struct binder_proc *target_proc)
+{
+ int ret = 0;
+
+ if (!info->enable) {
+ binder_inner_proc_lock(target_proc);
+ target_proc->sync_recv = false;
+ target_proc->async_recv = false;
+ target_proc->is_frozen = false;
+ binder_inner_proc_unlock(target_proc);
+ return 0;
+ }
+
+ /*
+ * Freezing the target. Prevent new transactions by
+ * setting frozen state. If timeout specified, wait
+ * for transactions to drain.
+ */
+ binder_inner_proc_lock(target_proc);
+ target_proc->sync_recv = false;
+ target_proc->async_recv = false;
+ target_proc->is_frozen = true;
+ binder_inner_proc_unlock(target_proc);
+
+ if (info->timeout_ms > 0)
+ ret = wait_event_interruptible_timeout(
+ target_proc->freeze_wait,
+ (!target_proc->outstanding_txns),
+ msecs_to_jiffies(info->timeout_ms));
+
+ if (!ret && target_proc->outstanding_txns)
+ ret = -EAGAIN;
+
+ if (ret < 0) {
+ binder_inner_proc_lock(target_proc);
+ target_proc->is_frozen = false;
+ binder_inner_proc_unlock(target_proc);
+ }
+
+ return ret;
+}
+
static int binder_ioctl_get_freezer_info(
struct binder_frozen_status_info *info)
{
struct binder_proc *target_proc;
bool found = false;
+ info->sync_recv = 0;
+ info->async_recv = 0;
+
mutex_lock(&binder_procs_lock);
hlist_for_each_entry(target_proc, &binder_procs, proc_node) {
if (target_proc->pid == info->pid) {
found = true;
binder_inner_proc_lock(target_proc);
- target_proc->tmp_ref++;
+ info->sync_recv |= target_proc->sync_recv;
+ info->async_recv |= target_proc->async_recv;
binder_inner_proc_unlock(target_proc);
- break;
}
}
mutex_unlock(&binder_procs_lock);
@@ -5008,13 +5054,6 @@ static int binder_ioctl_get_freezer_info(
if (!found)
return -EINVAL;
- binder_inner_proc_lock(target_proc);
- info->sync_recv = target_proc->sync_recv;
- info->async_recv = target_proc->async_recv;
- binder_inner_proc_unlock(target_proc);
-
- binder_proc_dec_tmpref(target_proc);
-
return 0;
}
@@ -5139,7 +5178,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case BINDER_FREEZE: {
struct binder_freeze_info info;
struct binder_proc *target_proc;
- bool found = false;
+ ret = -EINVAL;
if (copy_from_user(&info, ubuf, sizeof(info))) {
ret = -EFAULT;
@@ -5148,51 +5187,22 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mutex_lock(&binder_procs_lock);
hlist_for_each_entry(target_proc, &binder_procs, proc_node) {
if (target_proc->pid == info.pid) {
- found = true;
binder_inner_proc_lock(target_proc);
target_proc->tmp_ref++;
binder_inner_proc_unlock(target_proc);
- break;
+
+ mutex_unlock(&binder_procs_lock);
+ ret = binder_ioctl_freeze(&info, target_proc);
+ mutex_lock(&binder_procs_lock);
+
+ binder_proc_dec_tmpref(target_proc);
+
+ if (ret < 0)
+ break;
}
}
mutex_unlock(&binder_procs_lock);
- if (!found) {
- ret = -EINVAL;
- goto err;
- }
- if (!info.enable) {
- binder_inner_proc_lock(target_proc);
- target_proc->sync_recv = false;
- target_proc->async_recv = false;
- target_proc->is_frozen = false;
- binder_inner_proc_unlock(target_proc);
- binder_proc_dec_tmpref(target_proc);
- break;
- }
- /*
- * Freezing the target. Prevent new transactions by
- * setting frozen state. If timeout specified, wait
- * for transactions to drain.
- */
- binder_inner_proc_lock(target_proc);
- target_proc->sync_recv = false;
- target_proc->async_recv = false;
- target_proc->is_frozen = true;
- binder_inner_proc_unlock(target_proc);
- if (info.timeout_ms > 0)
- ret = wait_event_interruptible_timeout(
- target_proc->freeze_wait,
- (!target_proc->outstanding_txns),
- msecs_to_jiffies(info.timeout_ms));
- if (!ret && target_proc->outstanding_txns) {
- ret = -EAGAIN;
- }
- if (ret < 0) {
- binder_inner_proc_lock(target_proc);
- target_proc->is_frozen = false;
- binder_inner_proc_unlock(target_proc);
- }
- binder_proc_dec_tmpref(target_proc);
+
if (ret < 0)
goto err;
break;