static int binder_translate_fd_array()

in binder.c [2474:2537]


static int binder_translate_fd_array(struct list_head *pf_head,
				     struct binder_fd_array_object *fda,
				     const void __user *sender_ubuffer,
				     struct binder_buffer_object *parent,
				     struct binder_buffer_object *sender_uparent,
				     struct binder_transaction *t,
				     struct binder_thread *thread,
				     struct binder_transaction *in_reply_to)
{
	binder_size_t fdi, fd_buf_size;
	binder_size_t fda_offset;
	const void __user *sender_ufda_base;
	struct binder_proc *proc = thread->proc;
	int ret;

	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;
	}
	/*
	 * the source data for binder_buffer_object is visible
	 * to user-space and the @buffer element is the user
	 * pointer to the buffer_object containing the fd_array.
	 * Convert the address to an offset relative to
	 * the base of the transaction buffer.
	 */
	fda_offset = (parent->buffer - (uintptr_t)t->buffer->user_data) +
		fda->parent_offset;
	sender_ufda_base = (void __user *)(uintptr_t)sender_uparent->buffer +
				fda->parent_offset;

	if (!IS_ALIGNED((unsigned long)fda_offset, sizeof(u32)) ||
	    !IS_ALIGNED((unsigned long)sender_ufda_base, sizeof(u32))) {
		binder_user_error("%d:%d parent offset not aligned correctly.\n",
				  proc->pid, thread->pid);
		return -EINVAL;
	}
	ret = binder_add_fixup(pf_head, fda_offset, 0, fda->num_fds * sizeof(u32));
	if (ret)
		return ret;

	for (fdi = 0; fdi < fda->num_fds; fdi++) {
		u32 fd;
		binder_size_t offset = fda_offset + fdi * sizeof(fd);
		binder_size_t sender_uoffset = fdi * sizeof(fd);

		ret = copy_from_user(&fd, sender_ufda_base + sender_uoffset, sizeof(fd));
		if (!ret)
			ret = binder_translate_fd(fd, offset, t, thread,
						  in_reply_to);
		if (ret)
			return ret > 0 ? -EINVAL : ret;
	}
	return 0;
}