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) \
|
#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);
|
||||||
|
@ -33,6 +33,7 @@ enum {
|
|||||||
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
|
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_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
|
||||||
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', 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),
|
BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -129,6 +130,33 @@ enum {
|
|||||||
BINDER_BUFFER_FLAG_HAS_PARENT = 0x01,
|
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
|
* On 64-bit platforms where user code may run in 32-bits the driver must
|
||||||
* translate the buffer (and local binder) addresses appropriately.
|
* translate the buffer (and local binder) addresses appropriately.
|
||||||
|
Loading…
Reference in New Issue
Block a user