|
@@ -155,6 +155,9 @@ module_param_call(stop_on_user_error, binder_set_stop_on_user_error,
|
|
#define to_binder_buffer_object(hdr) \
|
|
#define to_binder_buffer_object(hdr) \
|
|
container_of(hdr, struct binder_buffer_object, hdr)
|
|
container_of(hdr, struct binder_buffer_object, hdr)
|
|
|
|
|
|
|
|
+#define to_binder_fd_array_object(hdr) \
|
|
|
|
+ container_of(hdr, struct binder_fd_array_object, hdr)
|
|
|
|
+
|
|
enum binder_stat_types {
|
|
enum binder_stat_types {
|
|
BINDER_STAT_PROC,
|
|
BINDER_STAT_PROC,
|
|
BINDER_STAT_THREAD,
|
|
BINDER_STAT_THREAD,
|
|
@@ -1310,6 +1313,9 @@ static size_t binder_validate_object(struct binder_buffer *buffer, u64 offset)
|
|
case BINDER_TYPE_PTR:
|
|
case BINDER_TYPE_PTR:
|
|
object_size = sizeof(struct binder_buffer_object);
|
|
object_size = sizeof(struct binder_buffer_object);
|
|
break;
|
|
break;
|
|
|
|
+ case BINDER_TYPE_FDA:
|
|
|
|
+ object_size = sizeof(struct binder_fd_array_object);
|
|
|
|
+ break;
|
|
default:
|
|
default:
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -1503,6 +1509,47 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|
* transaction buffer gets freed
|
|
* transaction buffer gets freed
|
|
*/
|
|
*/
|
|
break;
|
|
break;
|
|
|
|
+ case BINDER_TYPE_FDA: {
|
|
|
|
+ struct binder_fd_array_object *fda;
|
|
|
|
+ struct binder_buffer_object *parent;
|
|
|
|
+ uintptr_t parent_buffer;
|
|
|
|
+ u32 *fd_array;
|
|
|
|
+ size_t fd_index;
|
|
|
|
+ binder_size_t fd_buf_size;
|
|
|
|
+
|
|
|
|
+ fda = to_binder_fd_array_object(hdr);
|
|
|
|
+ parent = binder_validate_ptr(buffer, fda->parent,
|
|
|
|
+ off_start,
|
|
|
|
+ offp - off_start);
|
|
|
|
+ if (!parent) {
|
|
|
|
+ pr_err("transaction release %d bad parent offset",
|
|
|
|
+ debug_id);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ /*
|
|
|
|
+ * Since the parent was already fixed up, convert it
|
|
|
|
+ * back to kernel address space to access it
|
|
|
|
+ */
|
|
|
|
+ parent_buffer = parent->buffer -
|
|
|
|
+ proc->user_buffer_offset;
|
|
|
|
+
|
|
|
|
+ fd_buf_size = sizeof(u32) * fda->num_fds;
|
|
|
|
+ if (fda->num_fds >= SIZE_MAX / sizeof(u32)) {
|
|
|
|
+ pr_err("transaction release %d invalid number of fds (%lld)\n",
|
|
|
|
+ debug_id, (u64)fda->num_fds);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ if (fd_buf_size > parent->length ||
|
|
|
|
+ fda->parent_offset > parent->length - fd_buf_size) {
|
|
|
|
+ /* No space for all file descriptors here. */
|
|
|
|
+ pr_err("transaction release %d not enough space for %lld fds in buffer\n",
|
|
|
|
+ debug_id, (u64)fda->num_fds);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ fd_array = (u32 *)(parent_buffer + fda->parent_offset);
|
|
|
|
+ for (fd_index = 0; fd_index < fda->num_fds; fd_index++)
|
|
|
|
+ task_close_fd(proc, fd_array[fd_index]);
|
|
|
|
+ } break;
|
|
default:
|
|
default:
|
|
pr_err("transaction release %d bad object type %x\n",
|
|
pr_err("transaction release %d bad object type %x\n",
|
|
debug_id, hdr->type);
|
|
debug_id, hdr->type);
|
|
@@ -1672,6 +1719,63 @@ err_fd_not_accepted:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int binder_translate_fd_array(struct binder_fd_array_object *fda,
|
|
|
|
+ struct binder_buffer_object *parent,
|
|
|
|
+ struct binder_transaction *t,
|
|
|
|
+ struct binder_thread *thread,
|
|
|
|
+ struct binder_transaction *in_reply_to)
|
|
|
|
+{
|
|
|
|
+ binder_size_t fdi, fd_buf_size, num_installed_fds;
|
|
|
|
+ int target_fd;
|
|
|
|
+ uintptr_t parent_buffer;
|
|
|
|
+ u32 *fd_array;
|
|
|
|
+ struct binder_proc *proc = thread->proc;
|
|
|
|
+ struct binder_proc *target_proc = t->to_proc;
|
|
|
|
+
|
|
|
|
+ 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",
|
|
|
|
+ proc->pid, thread->pid, (u64)fda->num_fds);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ if (fd_buf_size > parent->length ||
|
|
|
|
+ fda->parent_offset > parent->length - fd_buf_size) {
|
|
|
|
+ /* No space for all file descriptors here. */
|
|
|
|
+ binder_user_error("%d:%d not enough space to store %lld fds in buffer\n",
|
|
|
|
+ proc->pid, thread->pid, (u64)fda->num_fds);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ /*
|
|
|
|
+ * Since the parent was already fixed up, convert it
|
|
|
|
+ * back to the kernel address space to access it
|
|
|
|
+ */
|
|
|
|
+ parent_buffer = parent->buffer - target_proc->user_buffer_offset;
|
|
|
|
+ fd_array = (u32 *)(parent_buffer + fda->parent_offset);
|
|
|
|
+ if (!IS_ALIGNED((unsigned long)fd_array, sizeof(u32))) {
|
|
|
|
+ binder_user_error("%d:%d parent offset not aligned correctly.\n",
|
|
|
|
+ proc->pid, thread->pid);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ for (fdi = 0; fdi < fda->num_fds; fdi++) {
|
|
|
|
+ target_fd = binder_translate_fd(fd_array[fdi], t, thread,
|
|
|
|
+ in_reply_to);
|
|
|
|
+ if (target_fd < 0)
|
|
|
|
+ goto err_translate_fd_failed;
|
|
|
|
+ fd_array[fdi] = target_fd;
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+err_translate_fd_failed:
|
|
|
|
+ /*
|
|
|
|
+ * Failed to allocate fd or security error, free fds
|
|
|
|
+ * installed so far.
|
|
|
|
+ */
|
|
|
|
+ num_installed_fds = fdi;
|
|
|
|
+ for (fdi = 0; fdi < num_installed_fds; fdi++)
|
|
|
|
+ task_close_fd(target_proc, fd_array[fdi]);
|
|
|
|
+ return target_fd;
|
|
|
|
+}
|
|
|
|
+
|
|
static int binder_fixup_parent(struct binder_transaction *t,
|
|
static int binder_fixup_parent(struct binder_transaction *t,
|
|
struct binder_thread *thread,
|
|
struct binder_thread *thread,
|
|
struct binder_buffer_object *bp,
|
|
struct binder_buffer_object *bp,
|
|
@@ -2000,6 +2104,38 @@ static void binder_transaction(struct binder_proc *proc,
|
|
fp->pad_binder = 0;
|
|
fp->pad_binder = 0;
|
|
fp->fd = target_fd;
|
|
fp->fd = target_fd;
|
|
} break;
|
|
} break;
|
|
|
|
+ case BINDER_TYPE_FDA: {
|
|
|
|
+ struct binder_fd_array_object *fda =
|
|
|
|
+ to_binder_fd_array_object(hdr);
|
|
|
|
+ struct binder_buffer_object *parent =
|
|
|
|
+ binder_validate_ptr(t->buffer, fda->parent,
|
|
|
|
+ off_start,
|
|
|
|
+ offp - off_start);
|
|
|
|
+ if (!parent) {
|
|
|
|
+ binder_user_error("%d:%d got transaction with invalid parent offset or type\n",
|
|
|
|
+ proc->pid, thread->pid);
|
|
|
|
+ return_error = BR_FAILED_REPLY;
|
|
|
|
+ goto err_bad_parent;
|
|
|
|
+ }
|
|
|
|
+ if (!binder_validate_fixup(t->buffer, off_start,
|
|
|
|
+ parent, fda->parent_offset,
|
|
|
|
+ last_fixup_obj,
|
|
|
|
+ last_fixup_min_off)) {
|
|
|
|
+ binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n",
|
|
|
|
+ proc->pid, thread->pid);
|
|
|
|
+ return_error = BR_FAILED_REPLY;
|
|
|
|
+ goto err_bad_parent;
|
|
|
|
+ }
|
|
|
|
+ ret = binder_translate_fd_array(fda, parent, t, thread,
|
|
|
|
+ in_reply_to);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ return_error = BR_FAILED_REPLY;
|
|
|
|
+ goto err_translate_failed;
|
|
|
|
+ }
|
|
|
|
+ last_fixup_obj = parent;
|
|
|
|
+ last_fixup_min_off =
|
|
|
|
+ fda->parent_offset + sizeof(u32) * fda->num_fds;
|
|
|
|
+ } break;
|
|
case BINDER_TYPE_PTR: {
|
|
case BINDER_TYPE_PTR: {
|
|
struct binder_buffer_object *bp =
|
|
struct binder_buffer_object *bp =
|
|
to_binder_buffer_object(hdr);
|
|
to_binder_buffer_object(hdr);
|
|
@@ -2070,6 +2206,7 @@ static void binder_transaction(struct binder_proc *proc,
|
|
err_translate_failed:
|
|
err_translate_failed:
|
|
err_bad_object_type:
|
|
err_bad_object_type:
|
|
err_bad_offset:
|
|
err_bad_offset:
|
|
|
|
+err_bad_parent:
|
|
err_copy_data_failed:
|
|
err_copy_data_failed:
|
|
trace_binder_transaction_failed_buffer_release(t->buffer);
|
|
trace_binder_transaction_failed_buffer_release(t->buffer);
|
|
binder_transaction_buffer_release(target_proc, t->buffer, offp);
|
|
binder_transaction_buffer_release(target_proc, t->buffer, offp);
|