binder: Add support for file-descriptor arrays

This patch introduces a new binder_fd_array object,
that allows us to support one or more file descriptors
embedded in a buffer that is scatter-gathered.

Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Martijn Coenen <maco@google.com>
Cc: Arve Hjønnevåg <arve@android.com>
Cc: Amit Pundir <amit.pundir@linaro.org>
Cc: Serban Constantinescu <serban.constantinescu@arm.com>
Cc: Dmitry Shmidt <dimitrysh@google.com>
Cc: Rom Lemarchand <romlem@google.com>
Cc: Android Kernel Team <kernel-team@android.com>
Signed-off-by: Martijn Coenen <maco@google.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Martijn Coenen 2017-02-03 14:40:52 -08:00 committed by Greg Kroah-Hartman
parent 7980240b6d
commit def95c7356
2 changed files with 165 additions and 0 deletions

View File

@ -155,6 +155,9 @@ module_param_call(stop_on_user_error, binder_set_stop_on_user_error,
#define to_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 {
BINDER_STAT_PROC,
BINDER_STAT_THREAD,
@ -1310,6 +1313,9 @@ static size_t binder_validate_object(struct binder_buffer *buffer, u64 offset)
case BINDER_TYPE_PTR:
object_size = sizeof(struct binder_buffer_object);
break;
case BINDER_TYPE_FDA:
object_size = sizeof(struct binder_fd_array_object);
break;
default:
return 0;
}
@ -1503,6 +1509,47 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
* transaction buffer gets freed
*/
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:
pr_err("transaction release %d bad object type %x\n",
debug_id, hdr->type);
@ -1672,6 +1719,63 @@ err_fd_not_accepted:
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,
struct binder_thread *thread,
struct binder_buffer_object *bp,
@ -2000,6 +2104,38 @@ static void binder_transaction(struct binder_proc *proc,
fp->pad_binder = 0;
fp->fd = target_fd;
} 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: {
struct binder_buffer_object *bp =
to_binder_buffer_object(hdr);
@ -2070,6 +2206,7 @@ static void binder_transaction(struct binder_proc *proc,
err_translate_failed:
err_bad_object_type:
err_bad_offset:
err_bad_parent:
err_copy_data_failed:
trace_binder_transaction_failed_buffer_release(t->buffer);
binder_transaction_buffer_release(target_proc, t->buffer, offp);

View File

@ -33,6 +33,7 @@ enum {
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
BINDER_TYPE_FDA = B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE),
BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),
};
@ -129,6 +130,33 @@ enum {
BINDER_BUFFER_FLAG_HAS_PARENT = 0x01,
};
/* struct binder_fd_array_object - object describing an array of fds in a buffer
* @hdr: common header structure
* @num_fds: number of file descriptors in the buffer
* @parent: index in offset array to buffer holding the fd array
* @parent_offset: start offset of fd array in the buffer
*
* A binder_fd_array object represents an array of file
* descriptors embedded in a binder_buffer_object. It is
* different from a regular binder_buffer_object because it
* describes a list of file descriptors to fix up, not an opaque
* blob of memory, and hence the kernel needs to treat it differently.
*
* An example of how this would be used is with Android's
* native_handle_t object, which is a struct with a list of integers
* and a list of file descriptors. The native_handle_t struct itself
* will be represented by a struct binder_buffer_objct, whereas the
* embedded list of file descriptors is represented by a
* struct binder_fd_array_object with that binder_buffer_object as
* a parent.
*/
struct binder_fd_array_object {
struct binder_object_header hdr;
binder_size_t num_fds;
binder_size_t parent;
binder_size_t parent_offset;
};
/*
* On 64-bit platforms where user code may run in 32-bits the driver must
* translate the buffer (and local binder) addresses appropriately.