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:
parent
7980240b6d
commit
def95c7356
@ -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);
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user