binder: refactor binder ref inc/dec for thread safety

Once locks are added, binder_ref's will only be accessed
safely with the proc lock held. Refactor the inc/dec paths
to make them atomic with the binder_get_ref* paths and
node inc/dec. For example, instead of:

  ref = binder_get_ref(proc, handle, strong);
  ...
  binder_dec_ref(ref, strong);

we now have:

  ret = binder_dec_ref_for_handle(proc, handle, strong, &rdata);

Since the actual ref is no longer exposed to callers, a
new struct binder_ref_data is introduced which can be used
to return a copy of ref state.

Signed-off-by: Todd Kjos <tkjos@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Todd Kjos 2017-06-29 12:01:58 -07:00 committed by Greg Kroah-Hartman
parent 7a4408c6bd
commit 372e3147df
2 changed files with 379 additions and 137 deletions

View File

@ -291,20 +291,51 @@ struct binder_ref_death {
binder_uintptr_t cookie; binder_uintptr_t cookie;
}; };
/**
* struct binder_ref_data - binder_ref counts and id
* @debug_id: unique ID for the ref
* @desc: unique userspace handle for ref
* @strong: strong ref count (debugging only if not locked)
* @weak: weak ref count (debugging only if not locked)
*
* Structure to hold ref count and ref id information. Since
* the actual ref can only be accessed with a lock, this structure
* is used to return information about the ref to callers of
* ref inc/dec functions.
*/
struct binder_ref_data {
int debug_id;
uint32_t desc;
int strong;
int weak;
};
/**
* struct binder_ref - struct to track references on nodes
* @data: binder_ref_data containing id, handle, and current refcounts
* @rb_node_desc: node for lookup by @data.desc in proc's rb_tree
* @rb_node_node: node for lookup by @node in proc's rb_tree
* @node_entry: list entry for node->refs list in target node
* @proc: binder_proc containing ref
* @node: binder_node of target node. When cleaning up a
* ref for deletion in binder_cleanup_ref, a non-NULL
* @node indicates the node must be freed
* @death: pointer to death notification (ref_death) if requested
*
* Structure to track references from procA to target node (on procB). This
* structure is unsafe to access without holding @proc->outer_lock.
*/
struct binder_ref { struct binder_ref {
/* Lookups needed: */ /* Lookups needed: */
/* node + proc => ref (transaction) */ /* node + proc => ref (transaction) */
/* desc + proc => ref (transaction, inc/dec ref) */ /* desc + proc => ref (transaction, inc/dec ref) */
/* node => refs + procs (proc exit) */ /* node => refs + procs (proc exit) */
int debug_id; struct binder_ref_data data;
struct rb_node rb_node_desc; struct rb_node rb_node_desc;
struct rb_node rb_node_node; struct rb_node rb_node_node;
struct hlist_node node_entry; struct hlist_node node_entry;
struct binder_proc *proc; struct binder_proc *proc;
struct binder_node *node; struct binder_node *node;
uint32_t desc;
int strong;
int weak;
struct binder_ref_death *death; struct binder_ref_death *death;
}; };
@ -627,11 +658,11 @@ static struct binder_ref *binder_get_ref(struct binder_proc *proc,
while (n) { while (n) {
ref = rb_entry(n, struct binder_ref, rb_node_desc); ref = rb_entry(n, struct binder_ref, rb_node_desc);
if (desc < ref->desc) { if (desc < ref->data.desc) {
n = n->rb_left; n = n->rb_left;
} else if (desc > ref->desc) { } else if (desc > ref->data.desc) {
n = n->rb_right; n = n->rb_right;
} else if (need_strong_ref && !ref->strong) { } else if (need_strong_ref && !ref->data.strong) {
binder_user_error("tried to use weak ref as strong ref\n"); binder_user_error("tried to use weak ref as strong ref\n");
return NULL; return NULL;
} else { } else {
@ -641,14 +672,33 @@ static struct binder_ref *binder_get_ref(struct binder_proc *proc,
return NULL; return NULL;
} }
/**
* binder_get_ref_for_node() - get the ref associated with given node
* @proc: binder_proc that owns the ref
* @node: binder_node of target
* @new_ref: newly allocated binder_ref to be initialized or %NULL
*
* Look up the ref for the given node and return it if it exists
*
* If it doesn't exist and the caller provides a newly allocated
* ref, initialize the fields of the newly allocated ref and insert
* into the given proc rb_trees and node refs list.
*
* Return: the ref for node. It is possible that another thread
* allocated/initialized the ref first in which case the
* returned ref would be different than the passed-in
* new_ref. new_ref must be kfree'd by the caller in
* this case.
*/
static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
struct binder_node *node) struct binder_node *node,
struct binder_ref *new_ref)
{ {
struct rb_node *n; struct binder_context *context = proc->context;
struct rb_node **p = &proc->refs_by_node.rb_node; struct rb_node **p = &proc->refs_by_node.rb_node;
struct rb_node *parent = NULL; struct rb_node *parent = NULL;
struct binder_ref *ref, *new_ref; struct binder_ref *ref;
struct binder_context *context = proc->context; struct rb_node *n;
while (*p) { while (*p) {
parent = *p; parent = *p;
@ -661,22 +711,22 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
else else
return ref; return ref;
} }
new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); if (!new_ref)
if (new_ref == NULL)
return NULL; return NULL;
binder_stats_created(BINDER_STAT_REF); binder_stats_created(BINDER_STAT_REF);
new_ref->debug_id = atomic_inc_return(&binder_last_id); new_ref->data.debug_id = atomic_inc_return(&binder_last_id);
new_ref->proc = proc; new_ref->proc = proc;
new_ref->node = node; new_ref->node = node;
rb_link_node(&new_ref->rb_node_node, parent, p); rb_link_node(&new_ref->rb_node_node, parent, p);
rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node); rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
new_ref->desc = (node == context->binder_context_mgr_node) ? 0 : 1; new_ref->data.desc = (node == context->binder_context_mgr_node) ? 0 : 1;
for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
ref = rb_entry(n, struct binder_ref, rb_node_desc); ref = rb_entry(n, struct binder_ref, rb_node_desc);
if (ref->desc > new_ref->desc) if (ref->data.desc > new_ref->data.desc)
break; break;
new_ref->desc = ref->desc + 1; new_ref->data.desc = ref->data.desc + 1;
} }
p = &proc->refs_by_desc.rb_node; p = &proc->refs_by_desc.rb_node;
@ -684,9 +734,9 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
parent = *p; parent = *p;
ref = rb_entry(parent, struct binder_ref, rb_node_desc); ref = rb_entry(parent, struct binder_ref, rb_node_desc);
if (new_ref->desc < ref->desc) if (new_ref->data.desc < ref->data.desc)
p = &(*p)->rb_left; p = &(*p)->rb_left;
else if (new_ref->desc > ref->desc) else if (new_ref->data.desc > ref->data.desc)
p = &(*p)->rb_right; p = &(*p)->rb_right;
else else
BUG(); BUG();
@ -697,89 +747,267 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
binder_debug(BINDER_DEBUG_INTERNAL_REFS, binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"%d new ref %d desc %d for node %d\n", "%d new ref %d desc %d for node %d\n",
proc->pid, new_ref->debug_id, new_ref->desc, proc->pid, new_ref->data.debug_id, new_ref->data.desc,
node->debug_id); node->debug_id);
return new_ref; return new_ref;
} }
static void binder_delete_ref(struct binder_ref *ref) static void binder_cleanup_ref(struct binder_ref *ref)
{ {
binder_debug(BINDER_DEBUG_INTERNAL_REFS, binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"%d delete ref %d desc %d for node %d\n", "%d delete ref %d desc %d for node %d\n",
ref->proc->pid, ref->debug_id, ref->desc, ref->proc->pid, ref->data.debug_id, ref->data.desc,
ref->node->debug_id); ref->node->debug_id);
rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc); rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node); rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);
if (ref->strong)
if (ref->data.strong)
binder_dec_node(ref->node, 1, 1); binder_dec_node(ref->node, 1, 1);
hlist_del(&ref->node_entry); hlist_del(&ref->node_entry);
binder_dec_node(ref->node, 0, 1); binder_dec_node(ref->node, 0, 1);
if (ref->death) { if (ref->death) {
binder_debug(BINDER_DEBUG_DEAD_BINDER, binder_debug(BINDER_DEBUG_DEAD_BINDER,
"%d delete ref %d desc %d has death notification\n", "%d delete ref %d desc %d has death notification\n",
ref->proc->pid, ref->debug_id, ref->desc); ref->proc->pid, ref->data.debug_id,
ref->data.desc);
list_del(&ref->death->work.entry); list_del(&ref->death->work.entry);
kfree(ref->death);
binder_stats_deleted(BINDER_STAT_DEATH); binder_stats_deleted(BINDER_STAT_DEATH);
} }
kfree(ref);
binder_stats_deleted(BINDER_STAT_REF); binder_stats_deleted(BINDER_STAT_REF);
} }
/**
* binder_inc_ref() - increment the ref for given handle
* @ref: ref to be incremented
* @strong: if true, strong increment, else weak
* @target_list: list to queue node work on
*
* Increment the ref.
*
* Return: 0, if successful, else errno
*/
static int binder_inc_ref(struct binder_ref *ref, int strong, static int binder_inc_ref(struct binder_ref *ref, int strong,
struct list_head *target_list) struct list_head *target_list)
{ {
int ret; int ret;
if (strong) { if (strong) {
if (ref->strong == 0) { if (ref->data.strong == 0) {
ret = binder_inc_node(ref->node, 1, 1, target_list); ret = binder_inc_node(ref->node, 1, 1, target_list);
if (ret) if (ret)
return ret; return ret;
} }
ref->strong++; ref->data.strong++;
} else { } else {
if (ref->weak == 0) { if (ref->data.weak == 0) {
ret = binder_inc_node(ref->node, 0, 1, target_list); ret = binder_inc_node(ref->node, 0, 1, target_list);
if (ret) if (ret)
return ret; return ret;
} }
ref->weak++; ref->data.weak++;
} }
return 0; return 0;
} }
/**
static int binder_dec_ref(struct binder_ref *ref, int strong) * binder_dec_ref() - dec the ref for given handle
* @ref: ref to be decremented
* @strong: if true, strong decrement, else weak
*
* Decrement the ref.
*
* TODO: kfree is avoided here since an upcoming patch
* will put this under a lock.
*
* Return: true if ref is cleaned up and ready to be freed
*/
static bool binder_dec_ref(struct binder_ref *ref, int strong)
{ {
if (strong) { if (strong) {
if (ref->strong == 0) { if (ref->data.strong == 0) {
binder_user_error("%d invalid dec strong, ref %d desc %d s %d w %d\n", binder_user_error("%d invalid dec strong, ref %d desc %d s %d w %d\n",
ref->proc->pid, ref->debug_id, ref->proc->pid, ref->data.debug_id,
ref->desc, ref->strong, ref->weak); ref->data.desc, ref->data.strong,
return -EINVAL; ref->data.weak);
return false;
} }
ref->strong--; ref->data.strong--;
if (ref->strong == 0) { if (ref->data.strong == 0) {
int ret; int ret;
ret = binder_dec_node(ref->node, strong, 1); ret = binder_dec_node(ref->node, strong, 1);
if (ret) if (ret)
return ret; return false;
} }
} else { } else {
if (ref->weak == 0) { if (ref->data.weak == 0) {
binder_user_error("%d invalid dec weak, ref %d desc %d s %d w %d\n", binder_user_error("%d invalid dec weak, ref %d desc %d s %d w %d\n",
ref->proc->pid, ref->debug_id, ref->proc->pid, ref->data.debug_id,
ref->desc, ref->strong, ref->weak); ref->data.desc, ref->data.strong,
return -EINVAL; ref->data.weak);
return false;
} }
ref->weak--; ref->data.weak--;
} }
if (ref->strong == 0 && ref->weak == 0) if (ref->data.strong == 0 && ref->data.weak == 0) {
binder_delete_ref(ref); binder_cleanup_ref(ref);
return 0; /*
* TODO: we could kfree(ref) here, but an upcoming
* patch will call this with a lock held, so we
* return an indication that the ref should be
* freed.
*/
return true;
}
return false;
}
/**
* binder_get_node_from_ref() - get the node from the given proc/desc
* @proc: proc containing the ref
* @desc: the handle associated with the ref
* @need_strong_ref: if true, only return node if ref is strong
* @rdata: the id/refcount data for the ref
*
* Given a proc and ref handle, return the associated binder_node
*
* Return: a binder_node or NULL if not found or not strong when strong required
*/
static struct binder_node *binder_get_node_from_ref(
struct binder_proc *proc,
u32 desc, bool need_strong_ref,
struct binder_ref_data *rdata)
{
struct binder_node *node;
struct binder_ref *ref;
ref = binder_get_ref(proc, desc, need_strong_ref);
if (!ref)
goto err_no_ref;
node = ref->node;
if (rdata)
*rdata = ref->data;
return node;
err_no_ref:
return NULL;
}
/**
* binder_free_ref() - free the binder_ref
* @ref: ref to free
*
* Free the binder_ref and the binder_ref_death indicated by ref->death.
*/
static void binder_free_ref(struct binder_ref *ref)
{
kfree(ref->death);
kfree(ref);
}
/**
* binder_update_ref_for_handle() - inc/dec the ref for given handle
* @proc: proc containing the ref
* @desc: the handle associated with the ref
* @increment: true=inc reference, false=dec reference
* @strong: true=strong reference, false=weak reference
* @rdata: the id/refcount data for the ref
*
* Given a proc and ref handle, increment or decrement the ref
* according to "increment" arg.
*
* Return: 0 if successful, else errno
*/
static int binder_update_ref_for_handle(struct binder_proc *proc,
uint32_t desc, bool increment, bool strong,
struct binder_ref_data *rdata)
{
int ret = 0;
struct binder_ref *ref;
bool delete_ref = false;
ref = binder_get_ref(proc, desc, strong);
if (!ref) {
ret = -EINVAL;
goto err_no_ref;
}
if (increment)
ret = binder_inc_ref(ref, strong, NULL);
else
delete_ref = binder_dec_ref(ref, strong);
if (rdata)
*rdata = ref->data;
if (delete_ref)
binder_free_ref(ref);
return ret;
err_no_ref:
return ret;
}
/**
* binder_dec_ref_for_handle() - dec the ref for given handle
* @proc: proc containing the ref
* @desc: the handle associated with the ref
* @strong: true=strong reference, false=weak reference
* @rdata: the id/refcount data for the ref
*
* Just calls binder_update_ref_for_handle() to decrement the ref.
*
* Return: 0 if successful, else errno
*/
static int binder_dec_ref_for_handle(struct binder_proc *proc,
uint32_t desc, bool strong, struct binder_ref_data *rdata)
{
return binder_update_ref_for_handle(proc, desc, false, strong, rdata);
}
/**
* binder_inc_ref_for_node() - increment the ref for given proc/node
* @proc: proc containing the ref
* @node: target node
* @strong: true=strong reference, false=weak reference
* @target_list: worklist to use if node is incremented
* @rdata: the id/refcount data for the ref
*
* Given a proc and node, increment the ref. Create the ref if it
* doesn't already exist
*
* Return: 0 if successful, else errno
*/
static int binder_inc_ref_for_node(struct binder_proc *proc,
struct binder_node *node,
bool strong,
struct list_head *target_list,
struct binder_ref_data *rdata)
{
struct binder_ref *ref;
struct binder_ref *new_ref = NULL;
int ret = 0;
ref = binder_get_ref_for_node(proc, node, NULL);
if (!ref) {
new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
if (!new_ref)
return -ENOMEM;
ref = binder_get_ref_for_node(proc, node, new_ref);
}
ret = binder_inc_ref(ref, strong, target_list);
*rdata = ref->data;
if (new_ref && ref != new_ref)
/*
* Another thread created the ref first so
* free the one we allocated
*/
kfree(new_ref);
return ret;
} }
static void binder_pop_transaction(struct binder_thread *target_thread, static void binder_pop_transaction(struct binder_thread *target_thread,
@ -1124,20 +1352,21 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
case BINDER_TYPE_HANDLE: case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: { case BINDER_TYPE_WEAK_HANDLE: {
struct flat_binder_object *fp; struct flat_binder_object *fp;
struct binder_ref *ref; struct binder_ref_data rdata;
int ret;
fp = to_flat_binder_object(hdr); fp = to_flat_binder_object(hdr);
ref = binder_get_ref(proc, fp->handle, ret = binder_dec_ref_for_handle(proc, fp->handle,
hdr->type == BINDER_TYPE_HANDLE); hdr->type == BINDER_TYPE_HANDLE, &rdata);
if (ref == NULL) {
pr_err("transaction release %d bad handle %d\n", if (ret) {
debug_id, fp->handle); pr_err("transaction release %d bad handle %d, ret = %d\n",
debug_id, fp->handle, ret);
break; break;
} }
binder_debug(BINDER_DEBUG_TRANSACTION, binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d (node %d)\n", " ref %d desc %d\n",
ref->debug_id, ref->desc, ref->node->debug_id); rdata.debug_id, rdata.desc);
binder_dec_ref(ref, hdr->type == BINDER_TYPE_HANDLE);
} break; } break;
case BINDER_TYPE_FD: { case BINDER_TYPE_FD: {
@ -1209,9 +1438,10 @@ static int binder_translate_binder(struct flat_binder_object *fp,
struct binder_thread *thread) struct binder_thread *thread)
{ {
struct binder_node *node; struct binder_node *node;
struct binder_ref *ref;
struct binder_proc *proc = thread->proc; struct binder_proc *proc = thread->proc;
struct binder_proc *target_proc = t->to_proc; struct binder_proc *target_proc = t->to_proc;
struct binder_ref_data rdata;
int ret;
node = binder_get_node(proc, fp->binder); node = binder_get_node(proc, fp->binder);
if (!node) { if (!node) {
@ -1232,25 +1462,25 @@ static int binder_translate_binder(struct flat_binder_object *fp,
if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) if (security_binder_transfer_binder(proc->tsk, target_proc->tsk))
return -EPERM; return -EPERM;
ref = binder_get_ref_for_node(target_proc, node); ret = binder_inc_ref_for_node(target_proc, node,
if (!ref) fp->hdr.type == BINDER_TYPE_BINDER,
return -ENOMEM; &thread->todo, &rdata);
if (ret)
return ret;
if (fp->hdr.type == BINDER_TYPE_BINDER) if (fp->hdr.type == BINDER_TYPE_BINDER)
fp->hdr.type = BINDER_TYPE_HANDLE; fp->hdr.type = BINDER_TYPE_HANDLE;
else else
fp->hdr.type = BINDER_TYPE_WEAK_HANDLE; fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
fp->binder = 0; fp->binder = 0;
fp->handle = ref->desc; fp->handle = rdata.desc;
fp->cookie = 0; fp->cookie = 0;
binder_inc_ref(ref, fp->hdr.type == BINDER_TYPE_HANDLE, &thread->todo);
trace_binder_transaction_node_to_ref(t, node, ref); trace_binder_transaction_node_to_ref(t, node, &rdata);
binder_debug(BINDER_DEBUG_TRANSACTION, binder_debug(BINDER_DEBUG_TRANSACTION,
" node %d u%016llx -> ref %d desc %d\n", " node %d u%016llx -> ref %d desc %d\n",
node->debug_id, (u64)node->ptr, node->debug_id, (u64)node->ptr,
ref->debug_id, ref->desc); rdata.debug_id, rdata.desc);
return 0; return 0;
} }
@ -1258,13 +1488,14 @@ static int binder_translate_handle(struct flat_binder_object *fp,
struct binder_transaction *t, struct binder_transaction *t,
struct binder_thread *thread) struct binder_thread *thread)
{ {
struct binder_ref *ref;
struct binder_proc *proc = thread->proc; struct binder_proc *proc = thread->proc;
struct binder_proc *target_proc = t->to_proc; struct binder_proc *target_proc = t->to_proc;
struct binder_node *node;
struct binder_ref_data src_rdata;
ref = binder_get_ref(proc, fp->handle, node = binder_get_node_from_ref(proc, fp->handle,
fp->hdr.type == BINDER_TYPE_HANDLE); fp->hdr.type == BINDER_TYPE_HANDLE, &src_rdata);
if (!ref) { if (!node) {
binder_user_error("%d:%d got transaction with invalid handle, %d\n", binder_user_error("%d:%d got transaction with invalid handle, %d\n",
proc->pid, thread->pid, fp->handle); proc->pid, thread->pid, fp->handle);
return -EINVAL; return -EINVAL;
@ -1272,37 +1503,41 @@ static int binder_translate_handle(struct flat_binder_object *fp,
if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) if (security_binder_transfer_binder(proc->tsk, target_proc->tsk))
return -EPERM; return -EPERM;
if (ref->node->proc == target_proc) { if (node->proc == target_proc) {
if (fp->hdr.type == BINDER_TYPE_HANDLE) if (fp->hdr.type == BINDER_TYPE_HANDLE)
fp->hdr.type = BINDER_TYPE_BINDER; fp->hdr.type = BINDER_TYPE_BINDER;
else else
fp->hdr.type = BINDER_TYPE_WEAK_BINDER; fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
fp->binder = ref->node->ptr; fp->binder = node->ptr;
fp->cookie = ref->node->cookie; fp->cookie = node->cookie;
binder_inc_node(ref->node, fp->hdr.type == BINDER_TYPE_BINDER, binder_inc_node(node,
fp->hdr.type == BINDER_TYPE_BINDER,
0, NULL); 0, NULL);
trace_binder_transaction_ref_to_node(t, ref); trace_binder_transaction_ref_to_node(t, node, &src_rdata);
binder_debug(BINDER_DEBUG_TRANSACTION, binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d -> node %d u%016llx\n", " ref %d desc %d -> node %d u%016llx\n",
ref->debug_id, ref->desc, ref->node->debug_id, src_rdata.debug_id, src_rdata.desc, node->debug_id,
(u64)ref->node->ptr); (u64)node->ptr);
} else { } else {
struct binder_ref *new_ref; int ret;
struct binder_ref_data dest_rdata;
new_ref = binder_get_ref_for_node(target_proc, ref->node); ret = binder_inc_ref_for_node(target_proc, node,
if (!new_ref) fp->hdr.type == BINDER_TYPE_HANDLE,
return -ENOMEM; NULL, &dest_rdata);
if (ret)
return ret;
fp->binder = 0; fp->binder = 0;
fp->handle = new_ref->desc; fp->handle = dest_rdata.desc;
fp->cookie = 0; fp->cookie = 0;
binder_inc_ref(new_ref, fp->hdr.type == BINDER_TYPE_HANDLE, trace_binder_transaction_ref_to_ref(t, node, &src_rdata,
NULL); &dest_rdata);
trace_binder_transaction_ref_to_ref(t, ref, new_ref);
binder_debug(BINDER_DEBUG_TRANSACTION, binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d -> ref %d desc %d (node %d)\n", " ref %d desc %d -> ref %d desc %d (node %d)\n",
ref->debug_id, ref->desc, new_ref->debug_id, src_rdata.debug_id, src_rdata.desc,
new_ref->desc, ref->node->debug_id); dest_rdata.debug_id, dest_rdata.desc,
node->debug_id);
} }
return 0; return 0;
} }
@ -2043,6 +2278,8 @@ static int binder_thread_write(struct binder_proc *proc,
void __user *end = buffer + size; void __user *end = buffer + size;
while (ptr < end && thread->return_error.cmd == BR_OK) { while (ptr < end && thread->return_error.cmd == BR_OK) {
int ret;
if (get_user(cmd, (uint32_t __user *)ptr)) if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT; return -EFAULT;
ptr += sizeof(uint32_t); ptr += sizeof(uint32_t);
@ -2058,62 +2295,61 @@ static int binder_thread_write(struct binder_proc *proc,
case BC_RELEASE: case BC_RELEASE:
case BC_DECREFS: { case BC_DECREFS: {
uint32_t target; uint32_t target;
struct binder_ref *ref = NULL;
const char *debug_string; const char *debug_string;
bool strong = cmd == BC_ACQUIRE || cmd == BC_RELEASE;
bool increment = cmd == BC_INCREFS || cmd == BC_ACQUIRE;
struct binder_ref_data rdata;
if (get_user(target, (uint32_t __user *)ptr)) if (get_user(target, (uint32_t __user *)ptr))
return -EFAULT; return -EFAULT;
ptr += sizeof(uint32_t); ptr += sizeof(uint32_t);
if (target == 0 && ret = -1;
(cmd == BC_INCREFS || cmd == BC_ACQUIRE)) { if (increment && !target) {
struct binder_node *ctx_mgr_node; struct binder_node *ctx_mgr_node;
mutex_lock(&context->context_mgr_node_lock); mutex_lock(&context->context_mgr_node_lock);
ctx_mgr_node = context->binder_context_mgr_node; ctx_mgr_node = context->binder_context_mgr_node;
if (ctx_mgr_node) { if (ctx_mgr_node)
ref = binder_get_ref_for_node(proc, ret = binder_inc_ref_for_node(
ctx_mgr_node); proc, ctx_mgr_node,
if (ref && ref->desc != target) { strong, NULL, &rdata);
binder_user_error("%d:%d tried to acquire reference to desc 0, got %d instead\n",
proc->pid, thread->pid,
ref->desc);
}
}
mutex_unlock(&context->context_mgr_node_lock); mutex_unlock(&context->context_mgr_node_lock);
} }
if (ref == NULL) if (ret)
ref = binder_get_ref(proc, target, ret = binder_update_ref_for_handle(
cmd == BC_ACQUIRE || proc, target, increment, strong,
cmd == BC_RELEASE); &rdata);
if (ref == NULL) { if (!ret && rdata.desc != target) {
binder_user_error("%d:%d refcount change on invalid ref %d\n", binder_user_error("%d:%d tried to acquire reference to desc %d, got %d instead\n",
proc->pid, thread->pid, target); proc->pid, thread->pid,
break; target, rdata.desc);
} }
switch (cmd) { switch (cmd) {
case BC_INCREFS: case BC_INCREFS:
debug_string = "IncRefs"; debug_string = "IncRefs";
binder_inc_ref(ref, 0, NULL);
break; break;
case BC_ACQUIRE: case BC_ACQUIRE:
debug_string = "Acquire"; debug_string = "Acquire";
binder_inc_ref(ref, 1, NULL);
break; break;
case BC_RELEASE: case BC_RELEASE:
debug_string = "Release"; debug_string = "Release";
binder_dec_ref(ref, 1);
break; break;
case BC_DECREFS: case BC_DECREFS:
default: default:
debug_string = "DecRefs"; debug_string = "DecRefs";
binder_dec_ref(ref, 0); break;
}
if (ret) {
binder_user_error("%d:%d %s %d refcount change on invalid ref %d ret %d\n",
proc->pid, thread->pid, debug_string,
strong, target, ret);
break; break;
} }
binder_debug(BINDER_DEBUG_USER_REFS, binder_debug(BINDER_DEBUG_USER_REFS,
"%d:%d %s ref %d desc %d s %d w %d for node %d\n", "%d:%d %s ref %d desc %d s %d w %d\n",
proc->pid, thread->pid, debug_string, ref->debug_id, proc->pid, thread->pid, debug_string,
ref->desc, ref->strong, ref->weak, ref->node->debug_id); rdata.debug_id, rdata.desc, rdata.strong,
rdata.weak);
break; break;
} }
case BC_INCREFS_DONE: case BC_INCREFS_DONE:
@ -2311,8 +2547,9 @@ static int binder_thread_write(struct binder_proc *proc,
cmd == BC_REQUEST_DEATH_NOTIFICATION ? cmd == BC_REQUEST_DEATH_NOTIFICATION ?
"BC_REQUEST_DEATH_NOTIFICATION" : "BC_REQUEST_DEATH_NOTIFICATION" :
"BC_CLEAR_DEATH_NOTIFICATION", "BC_CLEAR_DEATH_NOTIFICATION",
(u64)cookie, ref->debug_id, ref->desc, (u64)cookie, ref->data.debug_id,
ref->strong, ref->weak, ref->node->debug_id); ref->data.desc, ref->data.strong,
ref->data.weak, ref->node->debug_id);
if (cmd == BC_REQUEST_DEATH_NOTIFICATION) { if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
if (ref->death) { if (ref->death) {
@ -3473,7 +3710,8 @@ static void binder_deferred_release(struct binder_proc *proc)
ref = rb_entry(n, struct binder_ref, rb_node_desc); ref = rb_entry(n, struct binder_ref, rb_node_desc);
outgoing_refs++; outgoing_refs++;
binder_delete_ref(ref); binder_cleanup_ref(ref);
binder_free_ref(ref);
} }
binder_release_work(&proc->todo); binder_release_work(&proc->todo);
@ -3675,9 +3913,11 @@ static void print_binder_node(struct seq_file *m, struct binder_node *node)
static void print_binder_ref(struct seq_file *m, struct binder_ref *ref) static void print_binder_ref(struct seq_file *m, struct binder_ref *ref)
{ {
seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %p\n", seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %pK\n",
ref->debug_id, ref->desc, ref->node->proc ? "" : "dead ", ref->data.debug_id, ref->data.desc,
ref->node->debug_id, ref->strong, ref->weak, ref->death); ref->node->proc ? "" : "dead ",
ref->node->debug_id, ref->data.strong,
ref->data.weak, ref->death);
} }
static void print_binder_proc(struct seq_file *m, static void print_binder_proc(struct seq_file *m,
@ -3844,8 +4084,8 @@ static void print_binder_proc_stats(struct seq_file *m,
struct binder_ref *ref = rb_entry(n, struct binder_ref, struct binder_ref *ref = rb_entry(n, struct binder_ref,
rb_node_desc); rb_node_desc);
count++; count++;
strong += ref->strong; strong += ref->data.strong;
weak += ref->weak; weak += ref->data.weak;
} }
seq_printf(m, " refs: %d s %d w %d\n", count, strong, weak); seq_printf(m, " refs: %d s %d w %d\n", count, strong, weak);

View File

@ -24,7 +24,7 @@ struct binder_buffer;
struct binder_node; struct binder_node;
struct binder_proc; struct binder_proc;
struct binder_alloc; struct binder_alloc;
struct binder_ref; struct binder_ref_data;
struct binder_thread; struct binder_thread;
struct binder_transaction; struct binder_transaction;
@ -147,8 +147,8 @@ TRACE_EVENT(binder_transaction_received,
TRACE_EVENT(binder_transaction_node_to_ref, TRACE_EVENT(binder_transaction_node_to_ref,
TP_PROTO(struct binder_transaction *t, struct binder_node *node, TP_PROTO(struct binder_transaction *t, struct binder_node *node,
struct binder_ref *ref), struct binder_ref_data *rdata),
TP_ARGS(t, node, ref), TP_ARGS(t, node, rdata),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(int, debug_id) __field(int, debug_id)
@ -161,8 +161,8 @@ TRACE_EVENT(binder_transaction_node_to_ref,
__entry->debug_id = t->debug_id; __entry->debug_id = t->debug_id;
__entry->node_debug_id = node->debug_id; __entry->node_debug_id = node->debug_id;
__entry->node_ptr = node->ptr; __entry->node_ptr = node->ptr;
__entry->ref_debug_id = ref->debug_id; __entry->ref_debug_id = rdata->debug_id;
__entry->ref_desc = ref->desc; __entry->ref_desc = rdata->desc;
), ),
TP_printk("transaction=%d node=%d src_ptr=0x%016llx ==> dest_ref=%d dest_desc=%d", TP_printk("transaction=%d node=%d src_ptr=0x%016llx ==> dest_ref=%d dest_desc=%d",
__entry->debug_id, __entry->node_debug_id, __entry->debug_id, __entry->node_debug_id,
@ -171,8 +171,9 @@ TRACE_EVENT(binder_transaction_node_to_ref,
); );
TRACE_EVENT(binder_transaction_ref_to_node, TRACE_EVENT(binder_transaction_ref_to_node,
TP_PROTO(struct binder_transaction *t, struct binder_ref *ref), TP_PROTO(struct binder_transaction *t, struct binder_node *node,
TP_ARGS(t, ref), struct binder_ref_data *rdata),
TP_ARGS(t, node, rdata),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(int, debug_id) __field(int, debug_id)
@ -183,10 +184,10 @@ TRACE_EVENT(binder_transaction_ref_to_node,
), ),
TP_fast_assign( TP_fast_assign(
__entry->debug_id = t->debug_id; __entry->debug_id = t->debug_id;
__entry->ref_debug_id = ref->debug_id; __entry->ref_debug_id = rdata->debug_id;
__entry->ref_desc = ref->desc; __entry->ref_desc = rdata->desc;
__entry->node_debug_id = ref->node->debug_id; __entry->node_debug_id = node->debug_id;
__entry->node_ptr = ref->node->ptr; __entry->node_ptr = node->ptr;
), ),
TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%016llx", TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%016llx",
__entry->debug_id, __entry->node_debug_id, __entry->debug_id, __entry->node_debug_id,
@ -195,9 +196,10 @@ TRACE_EVENT(binder_transaction_ref_to_node,
); );
TRACE_EVENT(binder_transaction_ref_to_ref, TRACE_EVENT(binder_transaction_ref_to_ref,
TP_PROTO(struct binder_transaction *t, struct binder_ref *src_ref, TP_PROTO(struct binder_transaction *t, struct binder_node *node,
struct binder_ref *dest_ref), struct binder_ref_data *src_ref,
TP_ARGS(t, src_ref, dest_ref), struct binder_ref_data *dest_ref),
TP_ARGS(t, node, src_ref, dest_ref),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(int, debug_id) __field(int, debug_id)
@ -209,7 +211,7 @@ TRACE_EVENT(binder_transaction_ref_to_ref,
), ),
TP_fast_assign( TP_fast_assign(
__entry->debug_id = t->debug_id; __entry->debug_id = t->debug_id;
__entry->node_debug_id = src_ref->node->debug_id; __entry->node_debug_id = node->debug_id;
__entry->src_ref_debug_id = src_ref->debug_id; __entry->src_ref_debug_id = src_ref->debug_id;
__entry->src_ref_desc = src_ref->desc; __entry->src_ref_desc = src_ref->desc;
__entry->dest_ref_debug_id = dest_ref->debug_id; __entry->dest_ref_debug_id = dest_ref->debug_id;