Merge branch 'android11-5.4' into android11-5.4-lts
Do a back-merge to catch the latest -lts merge sync with the android11-5.4 branch. Changes included in here are: *b8b210d80aMerge tag 'android11-5.4.283_r00' into android11-5.4 *d62984adb1ANDROID: delete tool added by mistake *a03c6437cfANDROID: fix ENOMEM check of binder_proc_ext *be02156857ANDROID: binder: fix KMI issues due to frozen notification *1063c2fa62BACKPORT: FROMGIT: binder: frozen notification binder_features flag *d1e87637cdBACKPORT: FROMGIT: binder: frozen notification *8c4165a043BACKPORT: selftests/binderfs: add test for feature files *4d4f8b7a7fUPSTREAM: docs: binderfs: add section about feature files *460de65538BACKPORT: binderfs: add support for feature files *31f1f4b2aaFROMLIST: binder: fix memory leaks of spam and pending work *334fe73bddFROMGIT: Binder: add TF_UPDATE_TXN to replace outdated txn *2bfddf30aaBACKPORT: binder: tell userspace to dump current backtrace when detected oneway spamming Change-Id: Ib371e14baa94e83eacd571123d27b80a4d569d36 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
commit
4cf36217bc
9 changed files with 558 additions and 11 deletions
|
|
@ -66,3 +66,16 @@ that the `rm() <rm_>`_ tool can be used to delete them. Note that the
|
|||
``binder-control`` device cannot be deleted since this would make the binderfs
|
||||
instance unuseable. The ``binder-control`` device will be deleted when the
|
||||
binderfs instance is unmounted and all references to it have been dropped.
|
||||
|
||||
Binder features
|
||||
---------------
|
||||
|
||||
Assuming an instance of binderfs has been mounted at ``/dev/binderfs``, the
|
||||
features supported by the binder driver can be located under
|
||||
``/dev/binderfs/features/``. The presence of individual files can be tested
|
||||
to determine whether a particular feature is supported by the driver.
|
||||
|
||||
Example::
|
||||
|
||||
cat /dev/binderfs/features/oneway_spam_detection
|
||||
1
|
||||
|
|
|
|||
|
|
@ -1421,6 +1421,7 @@ static void binder_free_ref(struct binder_ref *ref)
|
|||
if (ref->node)
|
||||
binder_free_node(ref->node);
|
||||
kfree(ref->death);
|
||||
kfree(ref->freeze);
|
||||
kfree(ref);
|
||||
}
|
||||
|
||||
|
|
@ -2796,6 +2797,56 @@ static int binder_fixup_parent(struct list_head *pf_head,
|
|||
return binder_add_fixup(pf_head, buffer_offset, bp->buffer, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_can_update_transaction() - Can a txn be superseded by an updated one?
|
||||
* @t1: the pending async txn in the frozen process
|
||||
* @t2: the new async txn to supersede the outdated pending one
|
||||
*
|
||||
* Return: true if t2 can supersede t1
|
||||
* false if t2 can not supersede t1
|
||||
*/
|
||||
static bool binder_can_update_transaction(struct binder_transaction *t1,
|
||||
struct binder_transaction *t2)
|
||||
{
|
||||
if ((t1->flags & t2->flags & (TF_ONE_WAY | TF_UPDATE_TXN)) !=
|
||||
(TF_ONE_WAY | TF_UPDATE_TXN) || !t1->to_proc || !t2->to_proc)
|
||||
return false;
|
||||
if (t1->to_proc->tsk == t2->to_proc->tsk && t1->code == t2->code &&
|
||||
t1->flags == t2->flags && t1->buffer->pid == t2->buffer->pid &&
|
||||
t1->buffer->target_node->ptr == t2->buffer->target_node->ptr &&
|
||||
t1->buffer->target_node->cookie == t2->buffer->target_node->cookie)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_find_outdated_transaction_ilocked() - Find the outdated transaction
|
||||
* @t: new async transaction
|
||||
* @target_list: list to find outdated transaction
|
||||
*
|
||||
* Return: the outdated transaction if found
|
||||
* NULL if no outdated transacton can be found
|
||||
*
|
||||
* Requires the proc->inner_lock to be held.
|
||||
*/
|
||||
static struct binder_transaction *
|
||||
binder_find_outdated_transaction_ilocked(struct binder_transaction *t,
|
||||
struct list_head *target_list)
|
||||
{
|
||||
struct binder_work *w;
|
||||
|
||||
list_for_each_entry(w, target_list, entry) {
|
||||
struct binder_transaction *t_queued;
|
||||
|
||||
if (w->type != BINDER_WORK_TRANSACTION)
|
||||
continue;
|
||||
t_queued = container_of(w, struct binder_transaction, work);
|
||||
if (binder_can_update_transaction(t_queued, t))
|
||||
return t_queued;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_proc_transaction() - sends a transaction to a process and wakes it up
|
||||
* @t: transaction to send
|
||||
|
|
@ -2821,6 +2872,7 @@ static int binder_proc_transaction(struct binder_transaction *t,
|
|||
struct binder_node *node = t->buffer->target_node;
|
||||
bool oneway = !!(t->flags & TF_ONE_WAY);
|
||||
bool pending_async = false;
|
||||
struct binder_transaction *t_outdated = NULL;
|
||||
|
||||
BUG_ON(!node);
|
||||
binder_node_lock(node);
|
||||
|
|
@ -2856,6 +2908,17 @@ static int binder_proc_transaction(struct binder_transaction *t,
|
|||
} else if (!pending_async) {
|
||||
binder_enqueue_work_ilocked(&t->work, &proc->todo);
|
||||
} else {
|
||||
if ((t->flags & TF_UPDATE_TXN) && proc->is_frozen) {
|
||||
t_outdated = binder_find_outdated_transaction_ilocked(t,
|
||||
&node->async_todo);
|
||||
if (t_outdated) {
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
"txn %d supersedes %d\n",
|
||||
t->debug_id, t_outdated->debug_id);
|
||||
list_del_init(&t_outdated->work.entry);
|
||||
proc->outstanding_txns--;
|
||||
}
|
||||
}
|
||||
binder_enqueue_work_ilocked(&t->work, &node->async_todo);
|
||||
}
|
||||
|
||||
|
|
@ -2866,6 +2929,22 @@ static int binder_proc_transaction(struct binder_transaction *t,
|
|||
binder_inner_proc_unlock(proc);
|
||||
binder_node_unlock(node);
|
||||
|
||||
/*
|
||||
* To reduce potential contention, free the outdated transaction and
|
||||
* buffer after releasing the locks.
|
||||
*/
|
||||
if (t_outdated) {
|
||||
struct binder_buffer *buffer = t_outdated->buffer;
|
||||
|
||||
t_outdated->buffer = NULL;
|
||||
buffer->transaction = NULL;
|
||||
trace_binder_transaction_update_buffer_release(buffer);
|
||||
binder_transaction_buffer_release(proc, NULL, buffer, 0, 0);
|
||||
binder_alloc_free_buf(&proc->alloc, buffer);
|
||||
kfree(t_outdated);
|
||||
binder_stats_deleted(BINDER_STAT_TRANSACTION);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -3568,7 +3647,10 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
return_error_line = __LINE__;
|
||||
goto err_copy_data_failed;
|
||||
}
|
||||
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
|
||||
if (t->buffer->oneway_spam_suspect)
|
||||
tcomplete->type = BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT;
|
||||
else
|
||||
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
|
||||
t->work.type = BINDER_WORK_TRANSACTION;
|
||||
|
||||
if (reply) {
|
||||
|
|
@ -3717,6 +3799,154 @@ err_invalid_target_handle:
|
|||
}
|
||||
}
|
||||
|
||||
static int
|
||||
binder_request_freeze_notification(struct binder_proc *proc,
|
||||
struct binder_thread *thread,
|
||||
struct binder_handle_cookie *handle_cookie)
|
||||
{
|
||||
struct binder_ref_freeze *freeze;
|
||||
struct binder_ref *ref;
|
||||
bool is_frozen;
|
||||
|
||||
freeze = kzalloc(sizeof(*freeze), GFP_KERNEL);
|
||||
if (!freeze)
|
||||
return -ENOMEM;
|
||||
binder_proc_lock(proc);
|
||||
ref = binder_get_ref_olocked(proc, handle_cookie->handle, false);
|
||||
if (!ref) {
|
||||
binder_user_error("%d:%d BC_REQUEST_FREEZE_NOTIFICATION invalid ref %d\n",
|
||||
proc->pid, thread->pid, handle_cookie->handle);
|
||||
binder_proc_unlock(proc);
|
||||
kfree(freeze);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
binder_node_lock(ref->node);
|
||||
|
||||
if (ref->freeze || !ref->node->proc) {
|
||||
binder_user_error("%d:%d invalid BC_REQUEST_FREEZE_NOTIFICATION %s\n",
|
||||
proc->pid, thread->pid,
|
||||
ref->freeze ? "already set" : "dead node");
|
||||
binder_node_unlock(ref->node);
|
||||
binder_proc_unlock(proc);
|
||||
kfree(freeze);
|
||||
return -EINVAL;
|
||||
}
|
||||
binder_inner_proc_lock(ref->node->proc);
|
||||
is_frozen = ref->node->proc->is_frozen;
|
||||
binder_inner_proc_unlock(ref->node->proc);
|
||||
|
||||
INIT_LIST_HEAD(&freeze->work.entry);
|
||||
freeze->cookie = handle_cookie->cookie;
|
||||
freeze->work.type = BINDER_WORK_FROZEN_BINDER;
|
||||
freeze->is_frozen = is_frozen;
|
||||
|
||||
ref->freeze = freeze;
|
||||
|
||||
binder_inner_proc_lock(proc);
|
||||
binder_enqueue_work_ilocked(&ref->freeze->work, &proc->todo);
|
||||
binder_wakeup_proc_ilocked(proc);
|
||||
binder_inner_proc_unlock(proc);
|
||||
|
||||
binder_node_unlock(ref->node);
|
||||
binder_proc_unlock(proc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
binder_clear_freeze_notification(struct binder_proc *proc,
|
||||
struct binder_thread *thread,
|
||||
struct binder_handle_cookie *handle_cookie)
|
||||
{
|
||||
struct binder_ref_freeze *freeze;
|
||||
struct binder_ref *ref;
|
||||
|
||||
binder_proc_lock(proc);
|
||||
ref = binder_get_ref_olocked(proc, handle_cookie->handle, false);
|
||||
if (!ref) {
|
||||
binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION invalid ref %d\n",
|
||||
proc->pid, thread->pid, handle_cookie->handle);
|
||||
binder_proc_unlock(proc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
binder_node_lock(ref->node);
|
||||
|
||||
if (!ref->freeze) {
|
||||
binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n",
|
||||
proc->pid, thread->pid);
|
||||
binder_node_unlock(ref->node);
|
||||
binder_proc_unlock(proc);
|
||||
return -EINVAL;
|
||||
}
|
||||
freeze = ref->freeze;
|
||||
binder_inner_proc_lock(proc);
|
||||
if (freeze->cookie != handle_cookie->cookie) {
|
||||
binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch %016llx != %016llx\n",
|
||||
proc->pid, thread->pid, (u64)freeze->cookie,
|
||||
(u64)handle_cookie->cookie);
|
||||
binder_inner_proc_unlock(proc);
|
||||
binder_node_unlock(ref->node);
|
||||
binder_proc_unlock(proc);
|
||||
return -EINVAL;
|
||||
}
|
||||
ref->freeze = NULL;
|
||||
/*
|
||||
* Take the existing freeze object and overwrite its work type. There are three cases here:
|
||||
* 1. No pending notification. In this case just add the work to the queue.
|
||||
* 2. A notification was sent and is pending an ack from userspace. Once an ack arrives, we
|
||||
* should resend with the new work type.
|
||||
* 3. A notification is pending to be sent. Since the work is already in the queue, nothing
|
||||
* needs to be done here.
|
||||
*/
|
||||
freeze->work.type = BINDER_WORK_CLEAR_FREEZE_NOTIFICATION;
|
||||
if (list_empty(&freeze->work.entry)) {
|
||||
binder_enqueue_work_ilocked(&freeze->work, &proc->todo);
|
||||
binder_wakeup_proc_ilocked(proc);
|
||||
} else if (freeze->sent) {
|
||||
freeze->resend = true;
|
||||
}
|
||||
binder_inner_proc_unlock(proc);
|
||||
binder_node_unlock(ref->node);
|
||||
binder_proc_unlock(proc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
binder_freeze_notification_done(struct binder_proc *proc,
|
||||
struct binder_thread *thread,
|
||||
binder_uintptr_t cookie)
|
||||
{
|
||||
struct binder_ref_freeze *freeze = NULL;
|
||||
struct binder_work *w;
|
||||
|
||||
binder_inner_proc_lock(proc);
|
||||
list_for_each_entry(w, &proc_wrapper(proc)->delivered_freeze, entry) {
|
||||
struct binder_ref_freeze *tmp_freeze =
|
||||
container_of(w, struct binder_ref_freeze, work);
|
||||
|
||||
if (tmp_freeze->cookie == cookie) {
|
||||
freeze = tmp_freeze;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!freeze) {
|
||||
binder_user_error("%d:%d BC_FREEZE_NOTIFICATION_DONE %016llx not found\n",
|
||||
proc->pid, thread->pid, (u64)cookie);
|
||||
binder_inner_proc_unlock(proc);
|
||||
return -EINVAL;
|
||||
}
|
||||
binder_dequeue_work_ilocked(&freeze->work);
|
||||
freeze->sent = false;
|
||||
if (freeze->resend) {
|
||||
freeze->resend = false;
|
||||
binder_enqueue_work_ilocked(&freeze->work, &proc->todo);
|
||||
binder_wakeup_proc_ilocked(proc);
|
||||
}
|
||||
binder_inner_proc_unlock(proc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_free_buf() - free the specified buffer
|
||||
* @proc: binder proc that owns buffer
|
||||
|
|
@ -4192,6 +4422,44 @@ static int binder_thread_write(struct binder_proc *proc,
|
|||
binder_inner_proc_unlock(proc);
|
||||
} break;
|
||||
|
||||
case BC_REQUEST_FREEZE_NOTIFICATION: {
|
||||
struct binder_handle_cookie handle_cookie;
|
||||
int error;
|
||||
|
||||
if (copy_from_user(&handle_cookie, ptr, sizeof(handle_cookie)))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(handle_cookie);
|
||||
error = binder_request_freeze_notification(proc, thread,
|
||||
&handle_cookie);
|
||||
if (error)
|
||||
return error;
|
||||
} break;
|
||||
|
||||
case BC_CLEAR_FREEZE_NOTIFICATION: {
|
||||
struct binder_handle_cookie handle_cookie;
|
||||
int error;
|
||||
|
||||
if (copy_from_user(&handle_cookie, ptr, sizeof(handle_cookie)))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(handle_cookie);
|
||||
error = binder_clear_freeze_notification(proc, thread, &handle_cookie);
|
||||
if (error)
|
||||
return error;
|
||||
} break;
|
||||
|
||||
case BC_FREEZE_NOTIFICATION_DONE: {
|
||||
binder_uintptr_t cookie;
|
||||
int error;
|
||||
|
||||
if (get_user(cookie, (binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
|
||||
ptr += sizeof(cookie);
|
||||
error = binder_freeze_notification_done(proc, thread, cookie);
|
||||
if (error)
|
||||
return error;
|
||||
} break;
|
||||
|
||||
default:
|
||||
pr_err("%d:%d unknown command %d\n",
|
||||
proc->pid, thread->pid, cmd);
|
||||
|
|
@ -4445,9 +4713,14 @@ retry:
|
|||
|
||||
binder_stat_br(proc, thread, cmd);
|
||||
} break;
|
||||
case BINDER_WORK_TRANSACTION_COMPLETE: {
|
||||
case BINDER_WORK_TRANSACTION_COMPLETE:
|
||||
case BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT: {
|
||||
if (proc->oneway_spam_detection_enabled &&
|
||||
w->type == BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT)
|
||||
cmd = BR_ONEWAY_SPAM_SUSPECT;
|
||||
else
|
||||
cmd = BR_TRANSACTION_COMPLETE;
|
||||
binder_inner_proc_unlock(proc);
|
||||
cmd = BR_TRANSACTION_COMPLETE;
|
||||
kfree(w);
|
||||
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
|
||||
if (put_user(cmd, (uint32_t __user *)ptr))
|
||||
|
|
@ -4587,6 +4860,45 @@ retry:
|
|||
if (cmd == BR_DEAD_BINDER)
|
||||
goto done; /* DEAD_BINDER notifications can cause transactions */
|
||||
} break;
|
||||
|
||||
case BINDER_WORK_FROZEN_BINDER: {
|
||||
struct binder_ref_freeze *freeze;
|
||||
struct binder_frozen_state_info info;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
freeze = container_of(w, struct binder_ref_freeze, work);
|
||||
info.is_frozen = freeze->is_frozen;
|
||||
info.cookie = freeze->cookie;
|
||||
freeze->sent = true;
|
||||
binder_enqueue_work_ilocked(w, &proc_wrapper(proc)->delivered_freeze);
|
||||
binder_inner_proc_unlock(proc);
|
||||
|
||||
if (put_user(BR_FROZEN_BINDER, (uint32_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(uint32_t);
|
||||
if (copy_to_user(ptr, &info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(info);
|
||||
binder_stat_br(proc, thread, BR_FROZEN_BINDER);
|
||||
goto done; /* BR_FROZEN_BINDER notifications can cause transactions */
|
||||
} break;
|
||||
|
||||
case BINDER_WORK_CLEAR_FREEZE_NOTIFICATION: {
|
||||
struct binder_ref_freeze *freeze =
|
||||
container_of(w, struct binder_ref_freeze, work);
|
||||
binder_uintptr_t cookie = freeze->cookie;
|
||||
|
||||
binder_inner_proc_unlock(proc);
|
||||
kfree(freeze);
|
||||
if (put_user(BR_CLEAR_FREEZE_NOTIFICATION_DONE, (uint32_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(uint32_t);
|
||||
if (put_user(cookie, (binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
binder_stat_br(proc, thread, BR_CLEAR_FREEZE_NOTIFICATION_DONE);
|
||||
} break;
|
||||
|
||||
default:
|
||||
binder_inner_proc_unlock(proc);
|
||||
pr_err("%d:%d: bad work type %d\n",
|
||||
|
|
@ -4769,6 +5081,7 @@ static void binder_release_work(struct binder_proc *proc,
|
|||
"undelivered TRANSACTION_ERROR: %u\n",
|
||||
e->cmd);
|
||||
} break;
|
||||
case BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT:
|
||||
case BINDER_WORK_TRANSACTION_COMPLETE: {
|
||||
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
|
||||
"undelivered TRANSACTION_COMPLETE\n");
|
||||
|
|
@ -5198,6 +5511,48 @@ static bool binder_txns_pending_ilocked(struct binder_proc *proc)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void binder_add_freeze_work(struct binder_proc *proc, bool is_frozen)
|
||||
{
|
||||
struct rb_node *n;
|
||||
struct binder_ref *ref;
|
||||
|
||||
binder_inner_proc_lock(proc);
|
||||
for (n = rb_first(&proc->nodes); n; n = rb_next(n)) {
|
||||
struct binder_node *node;
|
||||
|
||||
node = rb_entry(n, struct binder_node, rb_node);
|
||||
binder_inner_proc_unlock(proc);
|
||||
binder_node_lock(node);
|
||||
hlist_for_each_entry(ref, &node->refs, node_entry) {
|
||||
/*
|
||||
* Need the node lock to synchronize
|
||||
* with new notification requests and the
|
||||
* inner lock to synchronize with queued
|
||||
* freeze notifications.
|
||||
*/
|
||||
binder_inner_proc_lock(ref->proc);
|
||||
if (!ref->freeze) {
|
||||
binder_inner_proc_unlock(ref->proc);
|
||||
continue;
|
||||
}
|
||||
ref->freeze->work.type = BINDER_WORK_FROZEN_BINDER;
|
||||
if (list_empty(&ref->freeze->work.entry)) {
|
||||
ref->freeze->is_frozen = is_frozen;
|
||||
binder_enqueue_work_ilocked(&ref->freeze->work, &ref->proc->todo);
|
||||
binder_wakeup_proc_ilocked(ref->proc);
|
||||
} else {
|
||||
if (ref->freeze->sent && ref->freeze->is_frozen != is_frozen)
|
||||
ref->freeze->resend = true;
|
||||
ref->freeze->is_frozen = is_frozen;
|
||||
}
|
||||
binder_inner_proc_unlock(ref->proc);
|
||||
}
|
||||
binder_node_unlock(node);
|
||||
binder_inner_proc_lock(proc);
|
||||
}
|
||||
binder_inner_proc_unlock(proc);
|
||||
}
|
||||
|
||||
static int binder_ioctl_freeze(struct binder_freeze_info *info,
|
||||
struct binder_proc *target_proc)
|
||||
{
|
||||
|
|
@ -5209,6 +5564,7 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info,
|
|||
target_proc->async_recv = false;
|
||||
target_proc->is_frozen = false;
|
||||
binder_inner_proc_unlock(target_proc);
|
||||
binder_add_freeze_work(target_proc, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -5241,6 +5597,8 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info,
|
|||
binder_inner_proc_lock(target_proc);
|
||||
target_proc->is_frozen = false;
|
||||
binder_inner_proc_unlock(target_proc);
|
||||
} else {
|
||||
binder_add_freeze_work(target_proc, true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -5472,6 +5830,18 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case BINDER_ENABLE_ONEWAY_SPAM_DETECTION: {
|
||||
uint32_t enable;
|
||||
|
||||
if (copy_from_user(&enable, ubuf, sizeof(enable))) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
binder_inner_proc_lock(proc);
|
||||
proc->oneway_spam_detection_enabled = (bool)enable;
|
||||
binder_inner_proc_unlock(proc);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
|
|
@ -5572,9 +5942,9 @@ static int binder_open(struct inode *nodp, struct file *filp)
|
|||
current->group_leader->pid, current->pid);
|
||||
|
||||
eproc = kzalloc(sizeof(*eproc), GFP_KERNEL);
|
||||
proc = &eproc->proc;
|
||||
if (proc == NULL)
|
||||
if (eproc == NULL)
|
||||
return -ENOMEM;
|
||||
proc = &eproc->proc;
|
||||
spin_lock_init(&proc->inner_lock);
|
||||
spin_lock_init(&proc->outer_lock);
|
||||
get_task_struct(current->group_leader);
|
||||
|
|
@ -5606,6 +5976,7 @@ static int binder_open(struct inode *nodp, struct file *filp)
|
|||
binder_stats_created(BINDER_STAT_PROC);
|
||||
proc->pid = current->group_leader->pid;
|
||||
INIT_LIST_HEAD(&proc->delivered_death);
|
||||
INIT_LIST_HEAD(&proc_wrapper(proc)->delivered_freeze);
|
||||
INIT_LIST_HEAD(&proc->waiting_threads);
|
||||
filp->private_data = proc;
|
||||
|
||||
|
|
@ -6158,6 +6529,7 @@ static const char * const binder_return_strings[] = {
|
|||
"BR_CLEAR_DEATH_NOTIFICATION_DONE",
|
||||
"BR_FAILED_REPLY",
|
||||
"BR_FROZEN_REPLY",
|
||||
"BR_ONEWAY_SPAM_SUSPECT",
|
||||
};
|
||||
|
||||
static const char * const binder_command_strings[] = {
|
||||
|
|
@ -6189,7 +6561,7 @@ static const char * const binder_objstat_strings[] = {
|
|||
"ref",
|
||||
"death",
|
||||
"transaction",
|
||||
"transaction_complete"
|
||||
"transaction_complete",
|
||||
};
|
||||
|
||||
static void print_binder_stats(struct seq_file *m, const char *prefix,
|
||||
|
|
|
|||
|
|
@ -339,7 +339,7 @@ static inline struct vm_area_struct *binder_alloc_get_vma(
|
|||
return vma;
|
||||
}
|
||||
|
||||
static void debug_low_async_space_locked(struct binder_alloc *alloc, int pid)
|
||||
static bool debug_low_async_space_locked(struct binder_alloc *alloc, int pid)
|
||||
{
|
||||
/*
|
||||
* Find the amount and size of buffers allocated by the current caller;
|
||||
|
|
@ -367,13 +367,19 @@ static void debug_low_async_space_locked(struct binder_alloc *alloc, int pid)
|
|||
|
||||
/*
|
||||
* Warn if this pid has more than 50 transactions, or more than 50% of
|
||||
* async space (which is 25% of total buffer size).
|
||||
* async space (which is 25% of total buffer size). Oneway spam is only
|
||||
* detected when the threshold is exceeded.
|
||||
*/
|
||||
if (num_buffers > 50 || total_alloc_size > alloc->buffer_size / 4) {
|
||||
binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
|
||||
"%d: pid %d spamming oneway? %zd buffers allocated for a total size of %zd\n",
|
||||
alloc->pid, pid, num_buffers, total_alloc_size);
|
||||
if (!alloc->oneway_spam_detected) {
|
||||
alloc->oneway_spam_detected = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct binder_buffer *binder_alloc_new_buf_locked(
|
||||
|
|
@ -526,6 +532,7 @@ static struct binder_buffer *binder_alloc_new_buf_locked(
|
|||
buffer->async_transaction = is_async;
|
||||
buffer->extra_buffers_size = extra_buffers_size;
|
||||
buffer->pid = pid;
|
||||
buffer->oneway_spam_suspect = false;
|
||||
if (is_async) {
|
||||
alloc->free_async_space -= size;
|
||||
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
|
||||
|
|
@ -537,7 +544,9 @@ static struct binder_buffer *binder_alloc_new_buf_locked(
|
|||
* of async space left (which is less than 10% of total
|
||||
* buffer size).
|
||||
*/
|
||||
debug_low_async_space_locked(alloc, pid);
|
||||
buffer->oneway_spam_suspect = debug_low_async_space_locked(alloc, pid);
|
||||
} else {
|
||||
alloc->oneway_spam_detected = false;
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ struct binder_transaction;
|
|||
* @clear_on_free: %true if buffer must be zeroed after use
|
||||
* @allow_user_free: %true if user is allowed to free buffer
|
||||
* @async_transaction: %true if buffer is in use for an async txn
|
||||
* @oneway_spam_suspect: %true if total async allocate size just exceed
|
||||
* spamming detect threshold
|
||||
* @debug_id: unique ID for debugging
|
||||
* @transaction: pointer to associated struct binder_transaction
|
||||
* @target_node: struct binder_node associated with this buffer
|
||||
|
|
@ -45,7 +47,8 @@ struct binder_buffer {
|
|||
unsigned clear_on_free:1;
|
||||
unsigned allow_user_free:1;
|
||||
unsigned async_transaction:1;
|
||||
unsigned debug_id:28;
|
||||
unsigned oneway_spam_suspect:1;
|
||||
unsigned debug_id:27;
|
||||
|
||||
struct binder_transaction *transaction;
|
||||
|
||||
|
|
@ -87,6 +90,8 @@ struct binder_lru_page {
|
|||
* @buffer_size: size of address space specified via mmap
|
||||
* @pid: pid for associated binder_proc (invariant after init)
|
||||
* @pages_high: high watermark of offset in @pages
|
||||
* @oneway_spam_detected: %true if oneway spam detection fired, clear that
|
||||
* flag once the async buffer has returned to a healthy state
|
||||
*
|
||||
* Bookkeeping structure for per-proc address space management for binder
|
||||
* buffers. It is normally initialized during binder_init() and binder_mmap()
|
||||
|
|
@ -107,6 +112,7 @@ struct binder_alloc {
|
|||
uint32_t buffer_free;
|
||||
int pid;
|
||||
size_t pages_high;
|
||||
bool oneway_spam_detected;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ANDROID_BINDER_IPC_SELFTEST
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ enum binder_stat_types {
|
|||
};
|
||||
|
||||
struct binder_stats {
|
||||
atomic_t br[_IOC_NR(BR_FROZEN_REPLY) + 1];
|
||||
atomic_t br[_IOC_NR(BR_ONEWAY_SPAM_SUSPECT) + 1];
|
||||
atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1];
|
||||
atomic_t obj_created[BINDER_STAT_COUNT];
|
||||
atomic_t obj_deleted[BINDER_STAT_COUNT];
|
||||
|
|
@ -174,11 +174,16 @@ struct binder_work {
|
|||
enum binder_work_type {
|
||||
BINDER_WORK_TRANSACTION = 1,
|
||||
BINDER_WORK_TRANSACTION_COMPLETE,
|
||||
BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT,
|
||||
BINDER_WORK_RETURN_ERROR,
|
||||
BINDER_WORK_NODE,
|
||||
BINDER_WORK_DEAD_BINDER,
|
||||
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
|
||||
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
|
||||
#ifndef __GENKSYMS__
|
||||
BINDER_WORK_FROZEN_BINDER,
|
||||
BINDER_WORK_CLEAR_FREEZE_NOTIFICATION,
|
||||
#endif
|
||||
} type;
|
||||
};
|
||||
|
||||
|
|
@ -300,6 +305,14 @@ struct binder_ref_death {
|
|||
binder_uintptr_t cookie;
|
||||
};
|
||||
|
||||
struct binder_ref_freeze {
|
||||
struct binder_work work;
|
||||
binder_uintptr_t cookie;
|
||||
bool is_frozen:1;
|
||||
bool sent:1;
|
||||
bool resend:1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct binder_ref_data - binder_ref counts and id
|
||||
* @debug_id: unique ID for the ref
|
||||
|
|
@ -332,6 +345,8 @@ struct binder_ref_data {
|
|||
* @node indicates the node must be freed
|
||||
* @death: pointer to death notification (ref_death) if requested
|
||||
* (protected by @node->lock)
|
||||
* @freeze: pointer to freeze notification (ref_freeze) if requested
|
||||
* (protected by @node->lock)
|
||||
*
|
||||
* Structure to track references from procA to target node (on procB). This
|
||||
* structure is unsafe to access without holding @proc->outer_lock.
|
||||
|
|
@ -348,6 +363,7 @@ struct binder_ref {
|
|||
struct binder_proc *proc;
|
||||
struct binder_node *node;
|
||||
struct binder_ref_death *death;
|
||||
struct binder_ref_freeze *freeze;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -438,6 +454,8 @@ enum binder_prio_state {
|
|||
* @outer_lock: no nesting under innor or node lock
|
||||
* Lock order: 1) outer, 2) node, 3) inner
|
||||
* @binderfs_entry: process-specific binderfs log file
|
||||
* @oneway_spam_detection_enabled: process enabled oneway spam detection
|
||||
* or not
|
||||
*
|
||||
* Bookkeeping structure for binder processes
|
||||
*/
|
||||
|
|
@ -473,6 +491,7 @@ struct binder_proc {
|
|||
spinlock_t inner_lock;
|
||||
spinlock_t outer_lock;
|
||||
struct dentry *binderfs_entry;
|
||||
bool oneway_spam_detection_enabled;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -481,6 +500,8 @@ struct binder_proc {
|
|||
* @cred struct cred associated with the `struct file`
|
||||
* in binder_open()
|
||||
* (invariant after initialized)
|
||||
* @delivered_freeze: list of delivered freeze notification
|
||||
* (protected by @inner_lock)
|
||||
*
|
||||
* Extended binder_proc -- needed to add the "cred" field without
|
||||
* changing the KMI for binder_proc.
|
||||
|
|
@ -488,6 +509,7 @@ struct binder_proc {
|
|||
struct binder_proc_ext {
|
||||
struct binder_proc proc;
|
||||
const struct cred *cred;
|
||||
struct list_head delivered_freeze;
|
||||
};
|
||||
|
||||
static inline const struct cred *binder_get_cred(struct binder_proc *proc)
|
||||
|
|
@ -498,6 +520,12 @@ static inline const struct cred *binder_get_cred(struct binder_proc *proc)
|
|||
return eproc->cred;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct binder_proc_ext *proc_wrapper(struct binder_proc *proc)
|
||||
{
|
||||
return container_of(proc, struct binder_proc_ext, proc);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct binder_thread - binder thread bookkeeping
|
||||
* @proc: binder process for this thread
|
||||
|
|
|
|||
|
|
@ -306,6 +306,10 @@ DEFINE_EVENT(binder_buffer_class, binder_transaction_failed_buffer_release,
|
|||
TP_PROTO(struct binder_buffer *buffer),
|
||||
TP_ARGS(buffer));
|
||||
|
||||
DEFINE_EVENT(binder_buffer_class, binder_transaction_update_buffer_release,
|
||||
TP_PROTO(struct binder_buffer *buffer),
|
||||
TP_ARGS(buffer));
|
||||
|
||||
TRACE_EVENT(binder_update_page_range,
|
||||
TP_PROTO(struct binder_alloc *alloc, bool allocate,
|
||||
void __user *start, void __user *end),
|
||||
|
|
|
|||
|
|
@ -59,12 +59,22 @@ enum binderfs_stats_mode {
|
|||
STATS_GLOBAL,
|
||||
};
|
||||
|
||||
struct binder_features {
|
||||
bool oneway_spam_detection;
|
||||
bool freeze_notification;
|
||||
};
|
||||
|
||||
static const match_table_t tokens = {
|
||||
{ Opt_max, "max=%d" },
|
||||
{ Opt_stats_mode, "stats=%s" },
|
||||
{ Opt_err, NULL }
|
||||
};
|
||||
|
||||
static struct binder_features binder_features = {
|
||||
.oneway_spam_detection = true,
|
||||
.freeze_notification = true,
|
||||
};
|
||||
|
||||
static inline struct binderfs_info *BINDERFS_I(const struct inode *inode)
|
||||
{
|
||||
return inode->i_sb->s_fs_info;
|
||||
|
|
@ -589,6 +599,39 @@ out:
|
|||
return dentry;
|
||||
}
|
||||
|
||||
static int binder_features_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
bool *feature = m->private;
|
||||
|
||||
seq_printf(m, "%d\n", *feature);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(binder_features);
|
||||
|
||||
static int init_binder_features(struct super_block *sb)
|
||||
{
|
||||
struct dentry *dentry, *dir;
|
||||
|
||||
dir = binderfs_create_dir(sb->s_root, "features");
|
||||
if (IS_ERR(dir))
|
||||
return PTR_ERR(dir);
|
||||
|
||||
dentry = binderfs_create_file(dir, "oneway_spam_detection",
|
||||
&binder_features_fops,
|
||||
&binder_features.oneway_spam_detection);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
|
||||
dentry = binderfs_create_file(dir, "freeze_notification",
|
||||
&binder_features_fops,
|
||||
&binder_features.freeze_notification);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_binder_logs(struct super_block *sb)
|
||||
{
|
||||
struct dentry *binder_logs_root_dir, *dentry, *proc_log_dir;
|
||||
|
|
@ -730,6 +773,10 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
|
|||
name++;
|
||||
}
|
||||
|
||||
ret = init_binder_features(sb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (info->mount_opts.stats_mode == STATS_GLOBAL)
|
||||
return init_binder_logs(sb);
|
||||
|
||||
|
|
|
|||
|
|
@ -284,6 +284,12 @@ struct binder_frozen_status_info {
|
|||
__u32 async_recv;
|
||||
};
|
||||
|
||||
struct binder_frozen_state_info {
|
||||
binder_uintptr_t cookie;
|
||||
__u32 is_frozen;
|
||||
__u32 reserved;
|
||||
};
|
||||
|
||||
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
|
||||
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64)
|
||||
#define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32)
|
||||
|
|
@ -296,6 +302,7 @@ struct binder_frozen_status_info {
|
|||
#define BINDER_SET_CONTEXT_MGR_EXT _IOW('b', 13, struct flat_binder_object)
|
||||
#define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info)
|
||||
#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info)
|
||||
#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
|
||||
|
||||
/*
|
||||
* NOTE: Two special error codes you should check for when calling
|
||||
|
|
@ -318,6 +325,7 @@ enum transaction_flags {
|
|||
TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */
|
||||
TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */
|
||||
TF_CLEAR_BUF = 0x20, /* clear buffer on txn complete */
|
||||
TF_UPDATE_TXN = 0x40, /* update the outdated pending async txn */
|
||||
};
|
||||
|
||||
struct binder_transaction_data {
|
||||
|
|
@ -483,6 +491,24 @@ enum binder_driver_return_protocol {
|
|||
* The target of the last transaction (either a bcTRANSACTION or
|
||||
* a bcATTEMPT_ACQUIRE) is frozen. No parameters.
|
||||
*/
|
||||
|
||||
BR_ONEWAY_SPAM_SUSPECT = _IO('r', 19),
|
||||
/*
|
||||
* Current process sent too many oneway calls to target, and the last
|
||||
* asynchronous transaction makes the allocated async buffer size exceed
|
||||
* detection threshold. No parameters.
|
||||
*/
|
||||
|
||||
BR_FROZEN_BINDER = _IOR('r', 21, struct binder_frozen_state_info),
|
||||
/*
|
||||
* The cookie and a boolean (is_frozen) that indicates whether the process
|
||||
* transitioned into a frozen or an unfrozen state.
|
||||
*/
|
||||
|
||||
BR_CLEAR_FREEZE_NOTIFICATION_DONE = _IOR('r', 22, binder_uintptr_t),
|
||||
/*
|
||||
* void *: cookie
|
||||
*/
|
||||
};
|
||||
|
||||
enum binder_driver_command_protocol {
|
||||
|
|
@ -566,6 +592,25 @@ enum binder_driver_command_protocol {
|
|||
/*
|
||||
* binder_transaction_data_sg: the sent command.
|
||||
*/
|
||||
|
||||
BC_REQUEST_FREEZE_NOTIFICATION =
|
||||
_IOW('c', 19, struct binder_handle_cookie),
|
||||
/*
|
||||
* int: handle
|
||||
* void *: cookie
|
||||
*/
|
||||
|
||||
BC_CLEAR_FREEZE_NOTIFICATION = _IOW('c', 20,
|
||||
struct binder_handle_cookie),
|
||||
/*
|
||||
* int: handle
|
||||
* void *: cookie
|
||||
*/
|
||||
|
||||
BC_FREEZE_NOTIFICATION_DONE = _IOW('c', 21, binder_uintptr_t),
|
||||
/*
|
||||
* void *: cookie
|
||||
*/
|
||||
};
|
||||
|
||||
#endif /* _UAPI_LINUX_BINDER_H */
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@
|
|||
#include <linux/android/binderfs.h>
|
||||
#include "../../kselftest.h"
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
#endif
|
||||
|
||||
static ssize_t write_nointr(int fd, const void *buf, size_t count)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
|
@ -140,6 +144,11 @@ static void __do_binderfs_test(void)
|
|||
bool keep = false;
|
||||
struct binderfs_device device = { 0 };
|
||||
struct binder_version version = { 0 };
|
||||
char device_path[sizeof("/dev/binderfs/") + BINDERFS_MAX_NAME];
|
||||
static const char * const binder_features[] = {
|
||||
"oneway_spam_detection",
|
||||
"freeze_notification",
|
||||
};
|
||||
|
||||
change_to_mountns();
|
||||
|
||||
|
|
@ -241,6 +250,20 @@ static void __do_binderfs_test(void)
|
|||
/* binder-control device removal failed as expected */
|
||||
ksft_inc_xfail_cnt();
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(binder_features); i++) {
|
||||
snprintf(device_path, sizeof(device_path),
|
||||
"/dev/binderfs/features/%s", binder_features[i]);
|
||||
fd = open(device_path, O_CLOEXEC | O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ksft_exit_fail_msg("%s - Failed to open binder feature: %s",
|
||||
strerror(errno), binder_features[i]);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* success: binder feature files found */
|
||||
ksft_inc_pass_cnt();
|
||||
|
||||
on_error:
|
||||
ret = umount2("/dev/binderfs", MNT_DETACH);
|
||||
keep ?: rmdir_protect_errno("/dev/binderfs");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue