summaryrefslogtreecommitdiff
path: root/drivers/android
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/android')
-rw-r--r--drivers/android/binder.c207
-rw-r--r--drivers/android/binder_alloc.c22
-rw-r--r--drivers/android/binder_internal.h5
-rw-r--r--drivers/android/binderfs.c8
4 files changed, 181 insertions, 61 deletions
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 73beea5dc18c..362c0deb65f1 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -133,18 +133,45 @@ static int binder_set_stop_on_user_error(const char *val,
module_param_call(stop_on_user_error, binder_set_stop_on_user_error,
param_get_int, &binder_stop_on_user_error, 0644);
-#define binder_debug(mask, x...) \
- do { \
- if (binder_debug_mask & mask) \
- pr_info_ratelimited(x); \
- } while (0)
+static __printf(2, 3) void binder_debug(int mask, const char *format, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ if (binder_debug_mask & mask) {
+ va_start(args, format);
+ vaf.va = &args;
+ vaf.fmt = format;
+ pr_info_ratelimited("%pV", &vaf);
+ va_end(args);
+ }
+}
+
+#define binder_txn_error(x...) \
+ binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, x)
+
+static __printf(1, 2) void binder_user_error(const char *format, ...)
+{
+ struct va_format vaf;
+ va_list args;
-#define binder_user_error(x...) \
+ if (binder_debug_mask & BINDER_DEBUG_USER_ERROR) {
+ va_start(args, format);
+ vaf.va = &args;
+ vaf.fmt = format;
+ pr_info_ratelimited("%pV", &vaf);
+ va_end(args);
+ }
+
+ if (binder_stop_on_user_error)
+ binder_stop_on_user_error = 2;
+}
+
+#define binder_set_extended_error(ee, _id, _command, _param) \
do { \
- if (binder_debug_mask & BINDER_DEBUG_USER_ERROR) \
- pr_info_ratelimited(x); \
- if (binder_stop_on_user_error) \
- binder_stop_on_user_error = 2; \
+ (ee)->id = _id; \
+ (ee)->command = _command; \
+ (ee)->param = _param; \
} while (0)
#define to_flat_binder_object(hdr) \
@@ -1481,6 +1508,8 @@ static void binder_free_txn_fixups(struct binder_transaction *t)
list_for_each_entry_safe(fixup, tmp, &t->fd_fixups, fixup_entry) {
fput(fixup->file);
+ if (fixup->target_fd >= 0)
+ put_unused_fd(fixup->target_fd);
list_del(&fixup->fixup_entry);
kfree(fixup);
}
@@ -2222,6 +2251,7 @@ static int binder_translate_fd(u32 fd, binder_size_t fd_offset,
}
fixup->file = file;
fixup->offset = fd_offset;
+ fixup->target_fd = -1;
trace_binder_transaction_fd_send(t, fd, fixup->offset);
list_add_tail(&fixup->fixup_entry, &t->fd_fixups);
@@ -2297,6 +2327,7 @@ static int binder_do_deferred_txn_copies(struct binder_alloc *alloc,
{
int ret = 0;
struct binder_sg_copy *sgc, *tmpsgc;
+ struct binder_ptr_fixup *tmppf;
struct binder_ptr_fixup *pf =
list_first_entry_or_null(pf_head, struct binder_ptr_fixup,
node);
@@ -2351,7 +2382,11 @@ static int binder_do_deferred_txn_copies(struct binder_alloc *alloc,
list_del(&sgc->node);
kfree(sgc);
}
- BUG_ON(!list_empty(pf_head));
+ list_for_each_entry_safe(pf, tmppf, pf_head, node) {
+ BUG_ON(pf->skip_size == 0);
+ list_del(&pf->node);
+ kfree(pf);
+ }
BUG_ON(!list_empty(sgc_head));
return ret > 0 ? -EINVAL : ret;
@@ -2488,6 +2523,9 @@ static int binder_translate_fd_array(struct list_head *pf_head,
struct binder_proc *proc = thread->proc;
int ret;
+ if (fda->num_fds == 0)
+ return 0;
+
fd_buf_size = sizeof(u32) * fda->num_fds;
if (fda->num_fds >= SIZE_MAX / sizeof(u32)) {
binder_user_error("%d:%d got transaction with invalid number of fds (%lld)\n",
@@ -2699,6 +2737,24 @@ static struct binder_node *binder_get_node_refs_for_txn(
return target_node;
}
+static void binder_set_txn_from_error(struct binder_transaction *t, int id,
+ uint32_t command, int32_t param)
+{
+ struct binder_thread *from = binder_get_txn_from_and_acq_inner(t);
+
+ if (!from) {
+ /* annotation for sparse */
+ __release(&from->proc->inner_lock);
+ return;
+ }
+
+ /* don't override existing errors */
+ if (from->ee.command == BR_OK)
+ binder_set_extended_error(&from->ee, id, command, param);
+ binder_inner_proc_unlock(from->proc);
+ binder_thread_dec_tmpref(from);
+}
+
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
@@ -2744,6 +2800,10 @@ static void binder_transaction(struct binder_proc *proc,
e->offsets_size = tr->offsets_size;
strscpy(e->context_name, proc->context->name, BINDERFS_MAX_NAME);
+ binder_inner_proc_lock(proc);
+ binder_set_extended_error(&thread->ee, t_debug_id, BR_OK, 0);
+ binder_inner_proc_unlock(proc);
+
if (reply) {
binder_inner_proc_lock(proc);
in_reply_to = thread->transaction_stack;
@@ -2779,6 +2839,8 @@ static void binder_transaction(struct binder_proc *proc,
if (target_thread == NULL) {
/* annotation for sparse */
__release(&target_thread->proc->inner_lock);
+ binder_txn_error("%d:%d reply target not found\n",
+ thread->pid, proc->pid);
return_error = BR_DEAD_REPLY;
return_error_line = __LINE__;
goto err_dead_binder;
@@ -2844,6 +2906,8 @@ static void binder_transaction(struct binder_proc *proc,
}
}
if (!target_node) {
+ binder_txn_error("%d:%d cannot find target node\n",
+ thread->pid, proc->pid);
/*
* return_error is set above
*/
@@ -2853,6 +2917,8 @@ static void binder_transaction(struct binder_proc *proc,
}
e->to_node = target_node->debug_id;
if (WARN_ON(proc == target_proc)) {
+ binder_txn_error("%d:%d self transactions not allowed\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = -EINVAL;
return_error_line = __LINE__;
@@ -2860,6 +2926,8 @@ static void binder_transaction(struct binder_proc *proc,
}
if (security_binder_transaction(proc->cred,
target_proc->cred) < 0) {
+ binder_txn_error("%d:%d transaction credentials failed\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = -EPERM;
return_error_line = __LINE__;
@@ -2931,6 +2999,8 @@ static void binder_transaction(struct binder_proc *proc,
/* TODO: reuse incoming transaction for reply */
t = kzalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL) {
+ binder_txn_error("%d:%d cannot allocate transaction\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = -ENOMEM;
return_error_line = __LINE__;
@@ -2942,6 +3012,8 @@ static void binder_transaction(struct binder_proc *proc,
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
if (tcomplete == NULL) {
+ binder_txn_error("%d:%d cannot allocate work for transaction\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = -ENOMEM;
return_error_line = __LINE__;
@@ -2988,6 +3060,8 @@ static void binder_transaction(struct binder_proc *proc,
security_cred_getsecid(proc->cred, &secid);
ret = security_secid_to_secctx(secid, &secctx, &secctx_sz);
if (ret) {
+ binder_txn_error("%d:%d failed to get security context\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
@@ -2996,7 +3070,8 @@ static void binder_transaction(struct binder_proc *proc,
added_size = ALIGN(secctx_sz, sizeof(u64));
extra_buffers_size += added_size;
if (extra_buffers_size < added_size) {
- /* integer overflow of extra_buffers_size */
+ binder_txn_error("%d:%d integer overflow of extra_buffers_size\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = -EINVAL;
return_error_line = __LINE__;
@@ -3010,9 +3085,15 @@ static void binder_transaction(struct binder_proc *proc,
tr->offsets_size, extra_buffers_size,
!reply && (t->flags & TF_ONE_WAY), current->tgid);
if (IS_ERR(t->buffer)) {
- /*
- * -ESRCH indicates VMA cleared. The target is dying.
- */
+ char *s;
+
+ ret = PTR_ERR(t->buffer);
+ s = (ret == -ESRCH) ? ": vma cleared, target dead or dying"
+ : (ret == -ENOSPC) ? ": no space left"
+ : (ret == -ENOMEM) ? ": memory allocation failed"
+ : "";
+ binder_txn_error("cannot allocate buffer%s", s);
+
return_error_param = PTR_ERR(t->buffer);
return_error = return_error_param == -ESRCH ?
BR_DEAD_REPLY : BR_FAILED_REPLY;
@@ -3095,6 +3176,8 @@ static void binder_transaction(struct binder_proc *proc,
t->buffer,
buffer_offset,
sizeof(object_offset))) {
+ binder_txn_error("%d:%d copy offset from buffer failed\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = -EINVAL;
return_error_line = __LINE__;
@@ -3153,6 +3236,8 @@ static void binder_transaction(struct binder_proc *proc,
t->buffer,
object_offset,
fp, sizeof(*fp))) {
+ binder_txn_error("%d:%d translate binder failed\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
@@ -3170,6 +3255,8 @@ static void binder_transaction(struct binder_proc *proc,
t->buffer,
object_offset,
fp, sizeof(*fp))) {
+ binder_txn_error("%d:%d translate handle failed\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
@@ -3190,6 +3277,8 @@ static void binder_transaction(struct binder_proc *proc,
t->buffer,
object_offset,
fp, sizeof(*fp))) {
+ binder_txn_error("%d:%d translate fd failed\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
@@ -3259,6 +3348,8 @@ static void binder_transaction(struct binder_proc *proc,
object_offset,
fda, sizeof(*fda));
if (ret) {
+ binder_txn_error("%d:%d translate fd array failed\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = ret > 0 ? -EINVAL : ret;
return_error_line = __LINE__;
@@ -3286,6 +3377,8 @@ static void binder_transaction(struct binder_proc *proc,
(const void __user *)(uintptr_t)bp->buffer,
bp->length);
if (ret) {
+ binder_txn_error("%d:%d deferred copy failed\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
@@ -3309,6 +3402,8 @@ static void binder_transaction(struct binder_proc *proc,
t->buffer,
object_offset,
bp, sizeof(*bp))) {
+ binder_txn_error("%d:%d failed to fixup parent\n",
+ thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
@@ -3416,6 +3511,8 @@ static void binder_transaction(struct binder_proc *proc,
return;
err_dead_proc_or_thread:
+ binder_txn_error("%d:%d dead process or thread\n",
+ thread->pid, proc->pid);
return_error_line = __LINE__;
binder_dequeue_work(proc, tcomplete);
err_translate_failed:
@@ -3451,21 +3548,26 @@ err_bad_call_stack:
err_empty_call_stack:
err_dead_binder:
err_invalid_target_handle:
- if (target_thread)
- binder_thread_dec_tmpref(target_thread);
- if (target_proc)
- binder_proc_dec_tmpref(target_proc);
if (target_node) {
binder_dec_node(target_node, 1, 0);
binder_dec_node_tmpref(target_node);
}
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
- "%d:%d transaction failed %d/%d, size %lld-%lld line %d\n",
- proc->pid, thread->pid, return_error, return_error_param,
+ "%d:%d transaction %s to %d:%d failed %d/%d/%d, size %lld-%lld line %d\n",
+ proc->pid, thread->pid, reply ? "reply" :
+ (tr->flags & TF_ONE_WAY ? "async" : "call"),
+ target_proc ? target_proc->pid : 0,
+ target_thread ? target_thread->pid : 0,
+ t_debug_id, return_error, return_error_param,
(u64)tr->data_size, (u64)tr->offsets_size,
return_error_line);
+ if (target_thread)
+ binder_thread_dec_tmpref(target_thread);
+ if (target_proc)
+ binder_proc_dec_tmpref(target_proc);
+
{
struct binder_transaction_log_entry *fe;
@@ -3485,10 +3587,16 @@ err_invalid_target_handle:
BUG_ON(thread->return_error.cmd != BR_OK);
if (in_reply_to) {
+ binder_set_txn_from_error(in_reply_to, t_debug_id,
+ return_error, return_error_param);
thread->return_error.cmd = BR_TRANSACTION_COMPLETE;
binder_enqueue_thread_work(thread, &thread->return_error.work);
binder_send_failed_reply(in_reply_to, return_error);
} else {
+ binder_inner_proc_lock(proc);
+ binder_set_extended_error(&thread->ee, t_debug_id,
+ return_error, return_error_param);
+ binder_inner_proc_unlock(proc);
thread->return_error.cmd = return_error;
binder_enqueue_thread_work(thread, &thread->return_error.work);
}
@@ -3978,7 +4086,7 @@ static int binder_thread_write(struct binder_proc *proc,
} break;
default:
- pr_err("%d:%d unknown command %d\n",
+ pr_err("%d:%d unknown command %u\n",
proc->pid, thread->pid, cmd);
return -EINVAL;
}
@@ -4069,10 +4177,9 @@ static int binder_wait_for_work(struct binder_thread *thread,
* Now that we are in the context of the transaction target
* process, we can allocate and install fds. Process the
* list of fds to translate and fixup the buffer with the
- * new fds.
+ * new fds first and only then install the files.
*
- * If we fail to allocate an fd, then free the resources by
- * fput'ing files that have not been processed and ksys_close'ing
+ * If we fail to allocate an fd, skip the install and release
* any fds that have already been allocated.
*/
static int binder_apply_fd_fixups(struct binder_proc *proc,
@@ -4089,41 +4196,31 @@ static int binder_apply_fd_fixups(struct binder_proc *proc,
"failed fd fixup txn %d fd %d\n",
t->debug_id, fd);
ret = -ENOMEM;
- break;
+ goto err;
}
binder_debug(BINDER_DEBUG_TRANSACTION,
"fd fixup txn %d fd %d\n",
t->debug_id, fd);
trace_binder_transaction_fd_recv(t, fd, fixup->offset);
- fd_install(fd, fixup->file);
- fixup->file = NULL;
+ fixup->target_fd = fd;
if (binder_alloc_copy_to_buffer(&proc->alloc, t->buffer,
fixup->offset, &fd,
sizeof(u32))) {
ret = -EINVAL;
- break;
+ goto err;
}
}
list_for_each_entry_safe(fixup, tmp, &t->fd_fixups, fixup_entry) {
- if (fixup->file) {
- fput(fixup->file);
- } else if (ret) {
- u32 fd;
- int err;
-
- err = binder_alloc_copy_from_buffer(&proc->alloc, &fd,
- t->buffer,
- fixup->offset,
- sizeof(fd));
- WARN_ON(err);
- if (!err)
- binder_deferred_fd_close(fd);
- }
+ fd_install(fixup->target_fd, fixup->file);
list_del(&fixup->fixup_entry);
kfree(fixup);
}
return ret;
+
+err:
+ binder_free_txn_fixups(t);
+ return ret;
}
static int binder_thread_read(struct binder_proc *proc,
@@ -4484,7 +4581,7 @@ retry:
trace_binder_transaction_received(t);
binder_stat_br(proc, thread, cmd);
binder_debug(BINDER_DEBUG_TRANSACTION,
- "%d:%d %s %d %d:%d, cmd %d size %zd-%zd ptr %016llx-%016llx\n",
+ "%d:%d %s %d %d:%d, cmd %u size %zd-%zd ptr %016llx-%016llx\n",
proc->pid, thread->pid,
(cmd == BR_TRANSACTION) ? "BR_TRANSACTION" :
(cmd == BR_TRANSACTION_SEC_CTX) ?
@@ -4626,6 +4723,7 @@ static struct binder_thread *binder_get_thread_ilocked(
thread->return_error.cmd = BR_OK;
thread->reply_error.work.type = BINDER_WORK_RETURN_ERROR;
thread->reply_error.cmd = BR_OK;
+ thread->ee.command = BR_OK;
INIT_LIST_HEAD(&new_thread->waiting_thread_node);
return thread;
}
@@ -5064,6 +5162,22 @@ static int binder_ioctl_get_freezer_info(
return 0;
}
+static int binder_ioctl_get_extended_error(struct binder_thread *thread,
+ void __user *ubuf)
+{
+ struct binder_extended_error ee;
+
+ binder_inner_proc_lock(thread->proc);
+ ee = thread->ee;
+ binder_set_extended_error(&thread->ee, 0, BR_OK, 0);
+ binder_inner_proc_unlock(thread->proc);
+
+ if (copy_to_user(ubuf, &ee, sizeof(ee)))
+ return -EFAULT;
+
+ return 0;
+}
+
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
@@ -5272,6 +5386,11 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
binder_inner_proc_unlock(proc);
break;
}
+ case BINDER_GET_EXTENDED_ERROR:
+ ret = binder_ioctl_get_extended_error(thread, ubuf);
+ if (ret < 0)
+ goto err;
+ break;
default:
ret = -EINVAL;
goto err;
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index 2ac1008a5f39..5649a0371a1f 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -1175,14 +1175,11 @@ static void binder_alloc_clear_buf(struct binder_alloc *alloc,
unsigned long size;
struct page *page;
pgoff_t pgoff;
- void *kptr;
page = binder_alloc_get_page(alloc, buffer,
buffer_offset, &pgoff);
size = min_t(size_t, bytes, PAGE_SIZE - pgoff);
- kptr = kmap(page) + pgoff;
- memset(kptr, 0, size);
- kunmap(page);
+ memset_page(page, pgoff, 0, size);
bytes -= size;
buffer_offset += size;
}
@@ -1220,9 +1217,9 @@ binder_alloc_copy_user_to_buffer(struct binder_alloc *alloc,
page = binder_alloc_get_page(alloc, buffer,
buffer_offset, &pgoff);
size = min_t(size_t, bytes, PAGE_SIZE - pgoff);
- kptr = kmap(page) + pgoff;
+ kptr = kmap_local_page(page) + pgoff;
ret = copy_from_user(kptr, from, size);
- kunmap(page);
+ kunmap_local(kptr);
if (ret)
return bytes - size + ret;
bytes -= size;
@@ -1247,23 +1244,14 @@ static int binder_alloc_do_buffer_copy(struct binder_alloc *alloc,
unsigned long size;
struct page *page;
pgoff_t pgoff;
- void *tmpptr;
- void *base_ptr;
page = binder_alloc_get_page(alloc, buffer,
buffer_offset, &pgoff);
size = min_t(size_t, bytes, PAGE_SIZE - pgoff);
- base_ptr = kmap_atomic(page);
- tmpptr = base_ptr + pgoff;
if (to_buffer)
- memcpy(tmpptr, ptr, size);
+ memcpy_to_page(page, pgoff, ptr, size);
else
- memcpy(ptr, tmpptr, size);
- /*
- * kunmap_atomic() takes care of flushing the cache
- * if this device has VIVT cache arch
- */
- kunmap_atomic(base_ptr);
+ memcpy_from_page(ptr, page, pgoff, size);
bytes -= size;
pgoff = 0;
ptr = ptr + size;
diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h
index d6b6b8cb7346..8dc0bccf8513 100644
--- a/drivers/android/binder_internal.h
+++ b/drivers/android/binder_internal.h
@@ -480,6 +480,8 @@ struct binder_proc {
* (only accessed by this thread)
* @reply_error: transaction errors reported by target thread
* (protected by @proc->inner_lock)
+ * @ee: extended error information from this thread
+ * (protected by @proc->inner_lock)
* @wait: wait queue for thread work
* @stats: per-thread statistics
* (atomics, no lock needed)
@@ -504,6 +506,7 @@ struct binder_thread {
bool process_todo;
struct binder_error return_error;
struct binder_error reply_error;
+ struct binder_extended_error ee;
wait_queue_head_t wait;
struct binder_stats stats;
atomic_t tmp_ref;
@@ -515,6 +518,7 @@ struct binder_thread {
* @fixup_entry: list entry
* @file: struct file to be associated with new fd
* @offset: offset in buffer data to this fixup
+ * @target_fd: fd to use by the target to install @file
*
* List element for fd fixups in a transaction. Since file
* descriptors need to be allocated in the context of the
@@ -525,6 +529,7 @@ struct binder_txn_fd_fixup {
struct list_head fixup_entry;
struct file *file;
size_t offset;
+ int target_fd;
};
struct binder_transaction {
diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
index e3605cdd4335..6c5e94f6cb3a 100644
--- a/drivers/android/binderfs.c
+++ b/drivers/android/binderfs.c
@@ -60,6 +60,7 @@ enum binderfs_stats_mode {
struct binder_features {
bool oneway_spam_detection;
+ bool extended_error;
};
static const struct constant_table binderfs_param_stats[] = {
@@ -75,6 +76,7 @@ static const struct fs_parameter_spec binderfs_fs_parameters[] = {
static struct binder_features binder_features = {
.oneway_spam_detection = true,
+ .extended_error = true,
};
static inline struct binderfs_info *BINDERFS_SB(const struct super_block *sb)
@@ -615,6 +617,12 @@ static int init_binder_features(struct super_block *sb)
if (IS_ERR(dentry))
return PTR_ERR(dentry);
+ dentry = binderfs_create_file(dir, "extended_error",
+ &binder_features_fops,
+ &binder_features.extended_error);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
return 0;
}