Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5fa02612 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "staging: android: binder: Add binder compat layer"

parents 86f0aee3 6176d309
Loading
Loading
Loading
Loading
+353 −2
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@
#include <linux/slab.h>
#include <linux/pid_namespace.h>
#include <linux/security.h>
#include <linux/compat.h>

#include "binder.h"
#include "binder_trace.h"
@@ -141,6 +142,77 @@ module_param_call(stop_on_user_error, binder_set_stop_on_user_error,
			binder_stop_on_user_error = 2; \
	} while (0)

#ifdef CONFIG_COMPAT
static inline void writeback_flat_binder_object(struct flat_binder_object *fp,
					 void __user *ptr)
{
	struct compat_flat_binder_object *tmp_fp;

	tmp_fp = (struct compat_flat_binder_object *) ptr;
	tmp_fp->type = fp->type;
	tmp_fp->flags = fp->flags;
	tmp_fp->binder = ptr_to_compat(fp->binder);
	tmp_fp->cookie = ptr_to_compat(fp->cookie);
}

static inline struct flat_binder_object *copy_flat_binder_object(void __user *ptr)
{
	struct flat_binder_object *fp;
	struct compat_flat_binder_object *tmp_fp;

	if (!is_compat_task())
		return (struct flat_binder_object *) ptr;

	fp = kzalloc(sizeof(*fp), GFP_KERNEL);
	if (fp == NULL)
		return NULL;

	tmp_fp = (struct compat_flat_binder_object *) ptr;
	/* copy compat struct */
	fp->type = tmp_fp->type;
	fp->flags = tmp_fp->flags;
	fp->binder = compat_ptr(tmp_fp->binder);
	fp->cookie = compat_ptr(tmp_fp->cookie);

	return fp;
}

static inline uint32_t compat_change_size(uint32_t cmd, size_t size)
{
	uint32_t compat_cmd;

	compat_cmd = cmd & ~IOCSIZE_MASK;
	compat_cmd = compat_cmd | ((size << _IOC_SIZESHIFT) & IOCSIZE_MASK);
	return compat_cmd;
}

#define align_helper(x) (						    \
	is_compat_task() ?						    \
	ALIGN(x, sizeof(compat_uptr_t)) :				    \
	ALIGN(x, sizeof(void *))					    \
	)

#define deref_helper(x) (						    \
	is_compat_task() ?						    \
	(size_t) *(compat_size_t *)x :					    \
	*(size_t *)x							    \
	)

#define size_helper(x) ({						    \
	size_t __size;							    \
	if (!is_compat_task())						    \
		__size = sizeof(x);					    \
	else if (sizeof(x) == sizeof(struct flat_binder_object))	    \
		__size = sizeof(struct compat_flat_binder_object);	    \
	else if (sizeof(x) == sizeof(struct binder_transaction_data))	    \
		__size = sizeof(struct compat_binder_transaction_data);	    \
	else if (sizeof(x) == sizeof(size_t))				    \
		__size = sizeof(compat_size_t);				    \
	else								    \
		 BUG();							    \
	__size;								    \
	})
#else
#define align_helper(ptr)	    ALIGN(ptr, sizeof(void *))
#define deref_helper(ptr)	    (*(typeof(size_t *))ptr)
#define size_helper(x)		    sizeof(x)
@@ -149,6 +221,7 @@ static inline struct flat_binder_object *copy_flat_binder_object(void __user *pt
{
	return (struct flat_binder_object *)ptr;
}
#endif

enum binder_stat_types {
	BINDER_STAT_PROC,
@@ -1255,7 +1328,7 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
	else
		off_end = (void *)offp + buffer->offsets_size;
	for (; offp < off_end; offp += size_helper(size_t)) {
		struct flat_binder_object *fp;
		struct flat_binder_object *fp = NULL;
		if (deref_helper(offp) > buffer->data_size - size_helper(*fp) ||
		    buffer->data_size < size_helper(*fp) ||
		    !IS_ALIGNED(deref_helper(offp), sizeof(u32))) {
@@ -1304,6 +1377,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
				debug_id, fp->type);
			break;
		}
		if (is_compat_task())
			kfree(fp);
	}
}

@@ -1322,6 +1397,7 @@ static void binder_transaction(struct binder_proc *proc,
	struct binder_transaction *in_reply_to = NULL;
	struct binder_transaction_log_entry *e;
	uint32_t return_error;
	struct flat_binder_object *fp = NULL;

	e = binder_transaction_log_add(&binder_transaction_log);
	e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
@@ -1508,7 +1584,6 @@ static void binder_transaction(struct binder_proc *proc,
	}
	off_end = (void *)offp + tr->offsets_size;
	for (; offp < off_end; offp += size_helper(size_t)) {
		struct flat_binder_object *fp;
		if (deref_helper(offp) > t->buffer->data_size - size_helper(*fp) ||
		    t->buffer->data_size < size_helper(*fp) ||
		    !IS_ALIGNED(deref_helper(offp), sizeof(u32))) {
@@ -1657,6 +1732,12 @@ static void binder_transaction(struct binder_proc *proc,
			return_error = BR_FAILED_REPLY;
			goto err_bad_object_type;
		}
#ifdef CONFIG_COMPAT
		if (is_compat_task()) {
			writeback_flat_binder_object(fp, t->buffer->data + deref_helper(offp));
			kfree(fp);
		}
#endif
	}
	if (reply) {
		BUG_ON(t->buffer->async_transaction != 0);
@@ -1692,6 +1773,8 @@ err_binder_new_node_failed:
err_bad_object_type:
err_bad_offset:
err_copy_data_failed:
	if (is_compat_task())
		kfree(fp);
	trace_binder_transaction_failed_buffer_release(t->buffer);
	binder_transaction_buffer_release(target_proc, t->buffer, offp);
	t->buffer->transaction = NULL;
@@ -1938,6 +2021,95 @@ static void bc_dead_binder_done(struct binder_proc *proc,
	return;
}

#ifdef CONFIG_COMPAT
static int compat_binder_thread_write(struct binder_proc *proc,
		struct binder_thread *thread, uint32_t cmd,
		void __user **ptr)
{
	BUG_ON(!is_compat_task());
	switch (cmd) {
	case COMPAT_BC_INCREFS_DONE:
	case COMPAT_BC_ACQUIRE_DONE: {
		compat_uptr_t node_ptr;
		compat_uptr_t cookie;

		if (get_user(node_ptr, (compat_uptr_t __user *)*ptr))
			return -EFAULT;
		*ptr += sizeof(compat_uptr_t);
		if (get_user(cookie, (compat_uptr_t __user *)*ptr))
			return -EFAULT;
		*ptr += sizeof(compat_uptr_t);
		bc_increfs_done(proc, thread, cmd == COMPAT_BC_ACQUIRE_DONE,
			compat_ptr(node_ptr), compat_ptr(cookie));
		break;
	}

	case COMPAT_BC_FREE_BUFFER: {
		compat_uptr_t data_ptr;

		if (get_user(data_ptr, (compat_uptr_t __user *)*ptr))
			return -EFAULT;
		*ptr += sizeof(compat_uptr_t);
		bc_free_buffer(proc, thread, compat_ptr(data_ptr));
		break;
	}

	case COMPAT_BC_TRANSACTION:
	case COMPAT_BC_REPLY: {
		struct binder_transaction_data tr;
		struct compat_binder_transaction_data tmp_tr;

		if (copy_from_user(&tmp_tr, *ptr, sizeof(tmp_tr)))
			return -EFAULT;
		*ptr += sizeof(tmp_tr);

		memset(&tr, 0, sizeof(tr));
		/* copy from compat struct */
		tr.target.ptr = compat_ptr(tmp_tr.target.ptr);
		tr.cookie = compat_ptr(tmp_tr.cookie);
		tr.code = tmp_tr.code;
		tr.flags = tmp_tr.flags;
		tr.sender_pid = tmp_tr.sender_pid;
		tr.sender_euid = tmp_tr.sender_euid;
		tr.data_size = (size_t) tmp_tr.data_size;
		tr.offsets_size = (size_t) tmp_tr.offsets_size;
		tr.data.ptr.buffer = compat_ptr(tmp_tr.data.ptr.buffer);
		tr.data.ptr.offsets = compat_ptr(tmp_tr.data.ptr.offsets);

		binder_transaction(proc, thread, &tr, cmd == COMPAT_BC_REPLY);
		break;
	}
	case COMPAT_BC_REQUEST_DEATH_NOTIFICATION:
	case COMPAT_BC_CLEAR_DEATH_NOTIFICATION: {
		uint32_t target;
		compat_uptr_t cookie;

		if (get_user(target, (uint32_t __user *)*ptr))
			return -EFAULT;
		*ptr += sizeof(uint32_t);
		if (get_user(cookie, (compat_uptr_t __user *)*ptr))
			return -EFAULT;
		*ptr += sizeof(compat_uptr_t);
		bc_clear_death_notif(proc, thread, cmd == COMPAT_BC_REQUEST_DEATH_NOTIFICATION,
			target, compat_ptr(cookie));
		break;
	}
	case COMPAT_BC_DEAD_BINDER_DONE: {
		compat_uptr_t cookie;

		if (get_user(cookie, (compat_uptr_t __user *)*ptr))
			return -EFAULT;
		*ptr += sizeof(compat_uptr_t);
		bc_dead_binder_done(proc, thread, compat_ptr(cookie));
		break;
	}
	default:
		return -EINVAL;
	}
	return 0;
}
#endif

static int binder_thread_write(struct binder_proc *proc,
			struct binder_thread *thread,
			void __user *buffer, size_t size, size_t *consumed)
@@ -2112,6 +2284,10 @@ static int binder_thread_write(struct binder_proc *proc,
			break;
		}
		default:
#ifdef CONFIG_COMPAT
			if (is_compat_task() && (!compat_binder_thread_write(proc, thread, cmd, &ptr)))
				break;
#endif
			pr_err("%d:%d unknown command %d\n",
			       proc->pid, thread->pid, cmd);
			return -EINVAL;
@@ -2145,6 +2321,103 @@ static int binder_has_thread_work(struct binder_thread *thread)
		(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
}

#ifdef CONFIG_COMPAT
static int binder_copy_to_user(uint32_t cmd, void *parcel,
			       void __user **ptr, size_t size)
{
	if (!is_compat_task()) {
		if (put_user(cmd, (uint32_t __user *)*ptr))
			return -EFAULT;
		*ptr += sizeof(uint32_t);
		if (copy_to_user(*ptr, parcel, size))
			return -EFAULT;
		*ptr += size;
		return 0;
	}
	switch (cmd) {
	case BR_INCREFS:
	case BR_ACQUIRE:
	case BR_RELEASE:
	case BR_DECREFS: {
		struct binder_ptr_cookie *fp;
		struct compat_binder_ptr_cookie tmp;

		cmd = compat_change_size(cmd, sizeof(tmp));
		BUG_ON((cmd != COMPAT_BR_INCREFS) &&
		       (cmd != COMPAT_BR_ACQUIRE) &&
		       (cmd != COMPAT_BR_RELEASE) &&
		       (cmd != COMPAT_BR_DECREFS));

		fp = (struct binder_ptr_cookie *) parcel;
		tmp.ptr = ptr_to_compat(fp->ptr);
		tmp.cookie = ptr_to_compat(fp->cookie);
		if (put_user(cmd, (uint32_t __user *)*ptr))
			return -EFAULT;
		*ptr += sizeof(uint32_t);
		if (copy_to_user(*ptr, &tmp, sizeof(tmp)))
			return -EFAULT;
		*ptr += sizeof(tmp);

		break;
	}
	case BR_DEAD_BINDER:
	case BR_CLEAR_DEATH_NOTIFICATION_DONE: {
		compat_uptr_t tmp;

		cmd = compat_change_size(cmd, sizeof(tmp));
		BUG_ON((cmd != COMPAT_BR_DEAD_BINDER) &&
		       (cmd != COMPAT_BR_CLEAR_DEATH_NOTIFICATION_DONE));

		tmp = ptr_to_compat((void *)*(uintptr_t *) parcel);
		if (put_user(cmd, (uint32_t __user *)*ptr))
			return -EFAULT;
		*ptr += sizeof(uint32_t);
		if (copy_to_user(*ptr, &tmp, sizeof(tmp)))
			return -EFAULT;
		*ptr += sizeof(tmp);

		break;
	}
	case BR_REPLY:
	case BR_TRANSACTION: {
		struct binder_transaction_data *fp;
		struct compat_binder_transaction_data tmp;

		memset(&tmp, 0, sizeof(tmp));
		cmd = compat_change_size(cmd, sizeof(tmp));
		BUG_ON((cmd != COMPAT_BR_REPLY) &&
		       (cmd != COMPAT_BR_TRANSACTION));

		fp = (struct binder_transaction_data *) parcel;
		/* copy to compat struct */
		tmp.target.ptr = ptr_to_compat(fp->target.ptr);
		tmp.cookie = ptr_to_compat(fp->cookie);
		tmp.code = fp->code;
		tmp.flags = fp->flags;
		tmp.sender_pid = fp->sender_pid;
		tmp.sender_euid = fp->sender_euid;
		tmp.data_size = (compat_size_t) fp->data_size;
		tmp.offsets_size = (compat_size_t) fp->offsets_size;
		tmp.data.ptr.buffer = ptr_to_compat((void *)fp->data.ptr.buffer);
		tmp.data.ptr.offsets = ptr_to_compat((void *)fp->data.ptr.offsets);

		if (put_user(cmd, (uint32_t __user *)*ptr))
			return -EFAULT;
		*ptr += sizeof(uint32_t);
		if (copy_to_user(*ptr, &tmp, sizeof(tmp)))
			return -EFAULT;
		*ptr += sizeof(tmp);

		break;
	}
	default:
		pr_err("unexpected user copy, cmd %d, ptr %p\n",
			cmd, (void *)*(uintptr_t *)ptr);
		return -EFAULT;
	}
	return 0;
}
#else
static int binder_copy_to_user(uint32_t cmd, void *parcel,
			       void __user **ptr, size_t size)
{
@@ -2156,6 +2429,7 @@ static int binder_copy_to_user(uint32_t cmd, void *parcel,
	*ptr += size;
	return 0;
}
#endif

static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
@@ -2750,6 +3024,80 @@ err_unlocked:
	return ret;
}

/* TODO: add tracepoint */
#ifdef CONFIG_COMPAT
static long compat_binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	struct binder_thread *thread;
	struct compat_binder_write_read bwr;
	struct binder_proc *proc = filp->private_data;
	void __user *ubuf = compat_ptr(arg);

	/* pr_info("compat_binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg); */

	if (cmd != COMPAT_BINDER_WRITE_READ)
		return binder_ioctl(filp, cmd, arg);

	BUG_ON(!is_compat_task());
	binder_lock(__func__);
	thread = binder_get_thread(proc);
	if (thread == NULL) {
		ret = -ENOMEM;
		goto err;
	}

	/* COMPAT_BINDER_WRITE_READ */
	if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
		ret = -EFAULT;
		goto err;
	}
	binder_debug(BINDER_DEBUG_READ_WRITE,
	    "compat: %d:%d write %d at %016x, read %d at %016x\n",
		    proc->pid, thread->pid, bwr.write_size,
		    bwr.write_buffer, bwr.read_size, bwr.read_buffer);

	if (bwr.write_size > 0) {
		size_t tmp_write_consumed = (size_t)bwr.write_consumed;
		ret = binder_thread_write(proc, thread, compat_ptr(bwr.write_buffer), (size_t)bwr.write_size, &tmp_write_consumed);
		bwr.write_consumed = (compat_size_t)tmp_write_consumed;
		if (ret < 0) {
			bwr.read_consumed = 0;
			if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
				ret = -EFAULT;
			goto err;
		}
	}

	if (bwr.read_size > 0) {
		size_t tmp_read_consumed = (size_t)bwr.read_consumed;
		ret = binder_thread_read(proc, thread, compat_ptr(bwr.read_buffer), (size_t)bwr.read_size, &tmp_read_consumed, filp->f_flags & O_NONBLOCK);
		bwr.read_consumed = (compat_size_t)tmp_read_consumed;
		if (!list_empty(&proc->todo))
			wake_up_interruptible(&proc->wait);
		if (ret < 0) {
			if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
				ret = -EFAULT;
			goto err;
		}
	}
	binder_debug(BINDER_DEBUG_READ_WRITE,
		    "compat: %d:%d wrote %d of %d, read return %d of %d\n",
		    proc->pid, thread->pid, bwr.write_consumed, bwr.write_size,
		    bwr.read_consumed, bwr.read_size);
	if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
		ret = -EFAULT;

err:
	if (thread)
		thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
	binder_unlock(__func__);
	if (ret && ret != -ERESTARTSYS)
		pr_info("%d:%d compat ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
	return ret;
}
#endif

static void binder_vma_open(struct vm_area_struct *vma)
{
	struct binder_proc *proc = vma->vm_private_data;
@@ -3563,6 +3911,9 @@ static const struct file_operations binder_fops = {
	.owner = THIS_MODULE,
	.poll = binder_poll,
	.unlocked_ioctl = binder_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = compat_binder_ioctl,
#endif
	.mmap = binder_mmap,
	.open = binder_open,
	.flush = binder_flush,