|
@@ -145,6 +145,11 @@ module_param_call(stop_on_user_error, binder_set_stop_on_user_error,
|
|
|
binder_stop_on_user_error = 2; \
|
|
|
} while (0)
|
|
|
|
|
|
+#define to_flat_binder_object(hdr) \
|
|
|
+ container_of(hdr, struct flat_binder_object, hdr)
|
|
|
+
|
|
|
+#define to_binder_fd_object(hdr) container_of(hdr, struct binder_fd_object, hdr)
|
|
|
+
|
|
|
enum binder_stat_types {
|
|
|
BINDER_STAT_PROC,
|
|
|
BINDER_STAT_THREAD,
|
|
@@ -1240,6 +1245,47 @@ static void binder_send_failed_reply(struct binder_transaction *t,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * binder_validate_object() - checks for a valid metadata object in a buffer.
|
|
|
+ * @buffer: binder_buffer that we're parsing.
|
|
|
+ * @offset: offset in the buffer at which to validate an object.
|
|
|
+ *
|
|
|
+ * Return: If there's a valid metadata object at @offset in @buffer, the
|
|
|
+ * size of that object. Otherwise, it returns zero.
|
|
|
+ */
|
|
|
+static size_t binder_validate_object(struct binder_buffer *buffer, u64 offset)
|
|
|
+{
|
|
|
+ /* Check if we can read a header first */
|
|
|
+ struct binder_object_header *hdr;
|
|
|
+ size_t object_size = 0;
|
|
|
+
|
|
|
+ if (offset > buffer->data_size - sizeof(*hdr) ||
|
|
|
+ buffer->data_size < sizeof(*hdr) ||
|
|
|
+ !IS_ALIGNED(offset, sizeof(u32)))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Ok, now see if we can read a complete object. */
|
|
|
+ hdr = (struct binder_object_header *)(buffer->data + offset);
|
|
|
+ switch (hdr->type) {
|
|
|
+ case BINDER_TYPE_BINDER:
|
|
|
+ case BINDER_TYPE_WEAK_BINDER:
|
|
|
+ case BINDER_TYPE_HANDLE:
|
|
|
+ case BINDER_TYPE_WEAK_HANDLE:
|
|
|
+ object_size = sizeof(struct flat_binder_object);
|
|
|
+ break;
|
|
|
+ case BINDER_TYPE_FD:
|
|
|
+ object_size = sizeof(struct binder_fd_object);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (offset <= buffer->data_size - object_size &&
|
|
|
+ buffer->data_size >= object_size)
|
|
|
+ return object_size;
|
|
|
+ else
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|
|
struct binder_buffer *buffer,
|
|
|
binder_size_t *failed_at)
|
|
@@ -1262,21 +1308,23 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|
|
else
|
|
|
off_end = (void *)offp + buffer->offsets_size;
|
|
|
for (; offp < off_end; offp++) {
|
|
|
- struct flat_binder_object *fp;
|
|
|
+ struct binder_object_header *hdr;
|
|
|
+ size_t object_size = binder_validate_object(buffer, *offp);
|
|
|
|
|
|
- if (*offp > buffer->data_size - sizeof(*fp) ||
|
|
|
- buffer->data_size < sizeof(*fp) ||
|
|
|
- !IS_ALIGNED(*offp, sizeof(u32))) {
|
|
|
- pr_err("transaction release %d bad offset %lld, size %zd\n",
|
|
|
+ if (object_size == 0) {
|
|
|
+ pr_err("transaction release %d bad object at offset %lld, size %zd\n",
|
|
|
debug_id, (u64)*offp, buffer->data_size);
|
|
|
continue;
|
|
|
}
|
|
|
- fp = (struct flat_binder_object *)(buffer->data + *offp);
|
|
|
- switch (fp->type) {
|
|
|
+ hdr = (struct binder_object_header *)(buffer->data + *offp);
|
|
|
+ switch (hdr->type) {
|
|
|
case BINDER_TYPE_BINDER:
|
|
|
case BINDER_TYPE_WEAK_BINDER: {
|
|
|
- struct binder_node *node = binder_get_node(proc, fp->binder);
|
|
|
+ struct flat_binder_object *fp;
|
|
|
+ struct binder_node *node;
|
|
|
|
|
|
+ fp = to_flat_binder_object(hdr);
|
|
|
+ node = binder_get_node(proc, fp->binder);
|
|
|
if (node == NULL) {
|
|
|
pr_err("transaction release %d bad node %016llx\n",
|
|
|
debug_id, (u64)fp->binder);
|
|
@@ -1285,15 +1333,17 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|
|
binder_debug(BINDER_DEBUG_TRANSACTION,
|
|
|
" node %d u%016llx\n",
|
|
|
node->debug_id, (u64)node->ptr);
|
|
|
- binder_dec_node(node, fp->type == BINDER_TYPE_BINDER, 0);
|
|
|
+ binder_dec_node(node, hdr->type == BINDER_TYPE_BINDER,
|
|
|
+ 0);
|
|
|
} break;
|
|
|
case BINDER_TYPE_HANDLE:
|
|
|
case BINDER_TYPE_WEAK_HANDLE: {
|
|
|
+ struct flat_binder_object *fp;
|
|
|
struct binder_ref *ref;
|
|
|
|
|
|
+ fp = to_flat_binder_object(hdr);
|
|
|
ref = binder_get_ref(proc, fp->handle,
|
|
|
- fp->type == BINDER_TYPE_HANDLE);
|
|
|
-
|
|
|
+ hdr->type == BINDER_TYPE_HANDLE);
|
|
|
if (ref == NULL) {
|
|
|
pr_err("transaction release %d bad handle %d\n",
|
|
|
debug_id, fp->handle);
|
|
@@ -1302,19 +1352,21 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|
|
binder_debug(BINDER_DEBUG_TRANSACTION,
|
|
|
" ref %d desc %d (node %d)\n",
|
|
|
ref->debug_id, ref->desc, ref->node->debug_id);
|
|
|
- binder_dec_ref(ref, fp->type == BINDER_TYPE_HANDLE);
|
|
|
+ binder_dec_ref(ref, hdr->type == BINDER_TYPE_HANDLE);
|
|
|
} break;
|
|
|
|
|
|
- case BINDER_TYPE_FD:
|
|
|
+ case BINDER_TYPE_FD: {
|
|
|
+ struct binder_fd_object *fp = to_binder_fd_object(hdr);
|
|
|
+
|
|
|
binder_debug(BINDER_DEBUG_TRANSACTION,
|
|
|
- " fd %d\n", fp->handle);
|
|
|
+ " fd %d\n", fp->fd);
|
|
|
if (failed_at)
|
|
|
- task_close_fd(proc, fp->handle);
|
|
|
- break;
|
|
|
+ task_close_fd(proc, fp->fd);
|
|
|
+ } break;
|
|
|
|
|
|
default:
|
|
|
pr_err("transaction release %d bad object type %x\n",
|
|
|
- debug_id, fp->type);
|
|
|
+ debug_id, hdr->type);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
@@ -1531,28 +1583,29 @@ static void binder_transaction(struct binder_proc *proc,
|
|
|
off_end = (void *)offp + tr->offsets_size;
|
|
|
off_min = 0;
|
|
|
for (; offp < off_end; offp++) {
|
|
|
- struct flat_binder_object *fp;
|
|
|
+ struct binder_object_header *hdr;
|
|
|
+ size_t object_size = binder_validate_object(t->buffer, *offp);
|
|
|
|
|
|
- if (*offp > t->buffer->data_size - sizeof(*fp) ||
|
|
|
- *offp < off_min ||
|
|
|
- t->buffer->data_size < sizeof(*fp) ||
|
|
|
- !IS_ALIGNED(*offp, sizeof(u32))) {
|
|
|
- binder_user_error("%d:%d got transaction with invalid offset, %lld (min %lld, max %lld)\n",
|
|
|
+ if (object_size == 0 || *offp < off_min) {
|
|
|
+ binder_user_error("%d:%d got transaction with invalid offset (%lld, min %lld max %lld) or object.\n",
|
|
|
proc->pid, thread->pid, (u64)*offp,
|
|
|
(u64)off_min,
|
|
|
- (u64)(t->buffer->data_size -
|
|
|
- sizeof(*fp)));
|
|
|
+ (u64)t->buffer->data_size);
|
|
|
return_error = BR_FAILED_REPLY;
|
|
|
goto err_bad_offset;
|
|
|
}
|
|
|
- fp = (struct flat_binder_object *)(t->buffer->data + *offp);
|
|
|
- off_min = *offp + sizeof(struct flat_binder_object);
|
|
|
- switch (fp->type) {
|
|
|
+
|
|
|
+ hdr = (struct binder_object_header *)(t->buffer->data + *offp);
|
|
|
+ off_min = *offp + object_size;
|
|
|
+ switch (hdr->type) {
|
|
|
case BINDER_TYPE_BINDER:
|
|
|
case BINDER_TYPE_WEAK_BINDER: {
|
|
|
+ struct flat_binder_object *fp;
|
|
|
+ struct binder_node *node;
|
|
|
struct binder_ref *ref;
|
|
|
- struct binder_node *node = binder_get_node(proc, fp->binder);
|
|
|
|
|
|
+ fp = to_flat_binder_object(hdr);
|
|
|
+ node = binder_get_node(proc, fp->binder);
|
|
|
if (node == NULL) {
|
|
|
node = binder_new_node(proc, fp->binder, fp->cookie);
|
|
|
if (node == NULL) {
|
|
@@ -1580,14 +1633,14 @@ static void binder_transaction(struct binder_proc *proc,
|
|
|
return_error = BR_FAILED_REPLY;
|
|
|
goto err_binder_get_ref_for_node_failed;
|
|
|
}
|
|
|
- if (fp->type == BINDER_TYPE_BINDER)
|
|
|
- fp->type = BINDER_TYPE_HANDLE;
|
|
|
+ if (hdr->type == BINDER_TYPE_BINDER)
|
|
|
+ hdr->type = BINDER_TYPE_HANDLE;
|
|
|
else
|
|
|
- fp->type = BINDER_TYPE_WEAK_HANDLE;
|
|
|
+ hdr->type = BINDER_TYPE_WEAK_HANDLE;
|
|
|
fp->binder = 0;
|
|
|
fp->handle = ref->desc;
|
|
|
fp->cookie = 0;
|
|
|
- binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
|
|
|
+ binder_inc_ref(ref, hdr->type == BINDER_TYPE_HANDLE,
|
|
|
&thread->todo);
|
|
|
|
|
|
trace_binder_transaction_node_to_ref(t, node, ref);
|
|
@@ -1598,11 +1651,12 @@ static void binder_transaction(struct binder_proc *proc,
|
|
|
} break;
|
|
|
case BINDER_TYPE_HANDLE:
|
|
|
case BINDER_TYPE_WEAK_HANDLE: {
|
|
|
+ struct flat_binder_object *fp;
|
|
|
struct binder_ref *ref;
|
|
|
|
|
|
+ fp = to_flat_binder_object(hdr);
|
|
|
ref = binder_get_ref(proc, fp->handle,
|
|
|
- fp->type == BINDER_TYPE_HANDLE);
|
|
|
-
|
|
|
+ hdr->type == BINDER_TYPE_HANDLE);
|
|
|
if (ref == NULL) {
|
|
|
binder_user_error("%d:%d got transaction with invalid handle, %d\n",
|
|
|
proc->pid,
|
|
@@ -1616,13 +1670,15 @@ static void binder_transaction(struct binder_proc *proc,
|
|
|
goto err_binder_get_ref_failed;
|
|
|
}
|
|
|
if (ref->node->proc == target_proc) {
|
|
|
- if (fp->type == BINDER_TYPE_HANDLE)
|
|
|
- fp->type = BINDER_TYPE_BINDER;
|
|
|
+ if (hdr->type == BINDER_TYPE_HANDLE)
|
|
|
+ hdr->type = BINDER_TYPE_BINDER;
|
|
|
else
|
|
|
- fp->type = BINDER_TYPE_WEAK_BINDER;
|
|
|
+ hdr->type = BINDER_TYPE_WEAK_BINDER;
|
|
|
fp->binder = ref->node->ptr;
|
|
|
fp->cookie = ref->node->cookie;
|
|
|
- binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
|
|
|
+ binder_inc_node(ref->node,
|
|
|
+ hdr->type == BINDER_TYPE_BINDER,
|
|
|
+ 0, NULL);
|
|
|
trace_binder_transaction_ref_to_node(t, ref);
|
|
|
binder_debug(BINDER_DEBUG_TRANSACTION,
|
|
|
" ref %d desc %d -> node %d u%016llx\n",
|
|
@@ -1639,7 +1695,9 @@ static void binder_transaction(struct binder_proc *proc,
|
|
|
fp->binder = 0;
|
|
|
fp->handle = new_ref->desc;
|
|
|
fp->cookie = 0;
|
|
|
- binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
|
|
|
+ binder_inc_ref(new_ref,
|
|
|
+ hdr->type == BINDER_TYPE_HANDLE,
|
|
|
+ NULL);
|
|
|
trace_binder_transaction_ref_to_ref(t, ref,
|
|
|
new_ref);
|
|
|
binder_debug(BINDER_DEBUG_TRANSACTION,
|
|
@@ -1652,25 +1710,26 @@ static void binder_transaction(struct binder_proc *proc,
|
|
|
case BINDER_TYPE_FD: {
|
|
|
int target_fd;
|
|
|
struct file *file;
|
|
|
+ struct binder_fd_object *fp = to_binder_fd_object(hdr);
|
|
|
|
|
|
if (reply) {
|
|
|
if (!(in_reply_to->flags & TF_ACCEPT_FDS)) {
|
|
|
binder_user_error("%d:%d got reply with fd, %d, but target does not allow fds\n",
|
|
|
- proc->pid, thread->pid, fp->handle);
|
|
|
+ proc->pid, thread->pid, fp->fd);
|
|
|
return_error = BR_FAILED_REPLY;
|
|
|
goto err_fd_not_allowed;
|
|
|
}
|
|
|
} else if (!target_node->accept_fds) {
|
|
|
binder_user_error("%d:%d got transaction with fd, %d, but target does not allow fds\n",
|
|
|
- proc->pid, thread->pid, fp->handle);
|
|
|
+ proc->pid, thread->pid, fp->fd);
|
|
|
return_error = BR_FAILED_REPLY;
|
|
|
goto err_fd_not_allowed;
|
|
|
}
|
|
|
|
|
|
- file = fget(fp->handle);
|
|
|
+ file = fget(fp->fd);
|
|
|
if (file == NULL) {
|
|
|
binder_user_error("%d:%d got transaction with invalid fd, %d\n",
|
|
|
- proc->pid, thread->pid, fp->handle);
|
|
|
+ proc->pid, thread->pid, fp->fd);
|
|
|
return_error = BR_FAILED_REPLY;
|
|
|
goto err_fget_failed;
|
|
|
}
|
|
@@ -1688,17 +1747,18 @@ static void binder_transaction(struct binder_proc *proc,
|
|
|
goto err_get_unused_fd_failed;
|
|
|
}
|
|
|
task_fd_install(target_proc, target_fd, file);
|
|
|
- trace_binder_transaction_fd(t, fp->handle, target_fd);
|
|
|
+ trace_binder_transaction_fd(t, fp->fd, target_fd);
|
|
|
binder_debug(BINDER_DEBUG_TRANSACTION,
|
|
|
- " fd %d -> %d\n", fp->handle, target_fd);
|
|
|
+ " fd %d -> %d\n", fp->fd,
|
|
|
+ target_fd);
|
|
|
/* TODO: fput? */
|
|
|
- fp->binder = 0;
|
|
|
- fp->handle = target_fd;
|
|
|
+ fp->pad_binder = 0;
|
|
|
+ fp->fd = target_fd;
|
|
|
} break;
|
|
|
|
|
|
default:
|
|
|
binder_user_error("%d:%d got transaction with invalid object type, %x\n",
|
|
|
- proc->pid, thread->pid, fp->type);
|
|
|
+ proc->pid, thread->pid, hdr->type);
|
|
|
return_error = BR_FAILED_REPLY;
|
|
|
goto err_bad_object_type;
|
|
|
}
|