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

Commit 90a570c9 authored by Todd Kjos's avatar Todd Kjos Committed by Todd Kjos
Browse files

BACKPORT: binder: add functions to copy to/from binder buffers



Avoid vm_area when copying to or from binder buffers.
Instead, new copy functions are added that copy from
kernel space to binder buffer space. These use
kmap_atomic() and kunmap_atomic() to create temporary
mappings and then memcpy() is used to copy within
that page.

Also, kmap_atomic() / kunmap_atomic() use the appropriate
cache flushing to support VIVT cache architectures.
Allow binder to build if CPU_CACHE_VIVT is defined.

Several uses of the new functions are added here. More
to follow in subsequent patches.

(cherry picked from commit 8ced0c6231ead26eca8cb416dcb7cc1c2cdd41d8)
Bug: 67668716
Change-Id: I6a93d2396d0a80c352a1d563fc7fb523a753e38c
Signed-off-by: default avatarTodd Kjos <tkjos@google.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d504949e
Loading
Loading
Loading
Loading
+70 −18
Original line number Diff line number Diff line
@@ -2369,14 +2369,22 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
		off_end = (void *)off_start + buffer->offsets_size;
	for (offp = off_start; offp < off_end; offp++) {
		struct binder_object_header *hdr;
		size_t object_size = binder_validate_object(buffer, *offp);

		size_t object_size;
		binder_size_t object_offset;
		binder_size_t buffer_offset = (uintptr_t)offp -
			(uintptr_t)buffer->data;

		binder_alloc_copy_from_buffer(&proc->alloc, &object_offset,
					      buffer, buffer_offset,
					      sizeof(object_offset));
		object_size = binder_validate_object(buffer, object_offset);
		if (object_size == 0) {
			pr_err("transaction release %d bad object at offset %lld, size %zd\n",
			       debug_id, (u64)*offp, buffer->data_size);
			       debug_id, (u64)object_offset, buffer->data_size);
			continue;
		}
		hdr = (struct binder_object_header *)(buffer->data + *offp);
		hdr = (struct binder_object_header *)
			(buffer->data + object_offset);
		switch (hdr->type) {
		case BINDER_TYPE_BINDER:
		case BINDER_TYPE_WEAK_BINDER: {
@@ -2470,8 +2478,20 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
				continue;
			}
			fd_array = (u32 *)(parent_buffer + (uintptr_t)fda->parent_offset);
			for (fd_index = 0; fd_index < fda->num_fds; fd_index++)
				task_close_fd(proc, fd_array[fd_index]);
			for (fd_index = 0; fd_index < fda->num_fds;
			     fd_index++) {
				u32 fd;
				binder_size_t offset =
					(uintptr_t)&fd_array[fd_index] -
					(uintptr_t)buffer->data;

				binder_alloc_copy_from_buffer(&proc->alloc,
							      &fd,
							      buffer,
							      offset,
							      sizeof(fd));
				task_close_fd(proc, fd);
			}
		} break;
		default:
			pr_err("transaction release %d bad object type %x\n",
@@ -2700,11 +2720,21 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda,
		return -EINVAL;
	}
	for (fdi = 0; fdi < fda->num_fds; fdi++) {
		target_fd = binder_translate_fd(fd_array[fdi], t, thread,
						in_reply_to);
		u32 fd;
		int target_fd;
		binder_size_t offset =
			(uintptr_t)&fd_array[fdi] -
			(uintptr_t)t->buffer->data;

		binder_alloc_copy_from_buffer(&target_proc->alloc,
					      &fd, t->buffer,
					      offset, sizeof(fd));
		target_fd = binder_translate_fd(fd, t, thread, in_reply_to);
		if (target_fd < 0)
			goto err_translate_fd_failed;
		fd_array[fdi] = target_fd;
		binder_alloc_copy_to_buffer(&target_proc->alloc,
					    t->buffer, offset,
					    &target_fd, sizeof(fd));
	}
	return 0;

@@ -2714,8 +2744,17 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda,
	 * installed so far.
	 */
	num_installed_fds = fdi;
	for (fdi = 0; fdi < num_installed_fds; fdi++)
		task_close_fd(target_proc, fd_array[fdi]);
	for (fdi = 0; fdi < num_installed_fds; fdi++) {
		u32 fd;
		binder_size_t offset =
			(uintptr_t)&fd_array[fdi] -
			(uintptr_t)t->buffer->data;

		binder_alloc_copy_from_buffer(&target_proc->alloc,
					      &fd, t->buffer,
					      offset, sizeof(fd));
		task_close_fd(target_proc, fd);
	}
	return target_fd;
}

@@ -3166,7 +3205,9 @@ static void binder_transaction(struct binder_proc *proc,

		t->security_ctx = (uintptr_t)kptr +
		    binder_alloc_get_user_buffer_offset(&target_proc->alloc);
		memcpy(kptr, secctx, secctx_sz);
		binder_alloc_copy_to_buffer(&target_proc->alloc,
					    t->buffer, buf_offset,
					    secctx, secctx_sz);
		security_release_secctx(secctx, secctx_sz);
		secctx = NULL;
	}
@@ -3228,11 +3269,21 @@ static void binder_transaction(struct binder_proc *proc,
	off_min = 0;
	for (; offp < off_end; offp++) {
		struct binder_object_header *hdr;
		size_t object_size = binder_validate_object(t->buffer, *offp);
		size_t object_size;
		binder_size_t object_offset;
		binder_size_t buffer_offset =
			(uintptr_t)offp - (uintptr_t)t->buffer->data;

		if (object_size == 0 || *offp < off_min) {
		binder_alloc_copy_from_buffer(&target_proc->alloc,
					      &object_offset,
					      t->buffer,
					      buffer_offset,
					      sizeof(object_offset));
		object_size = binder_validate_object(t->buffer, object_offset);
		if (object_size == 0 || object_offset < off_min) {
			binder_user_error("%d:%d got transaction with invalid offset (%lld, min %lld max %lld) or object.\n",
					  proc->pid, thread->pid, (u64)*offp,
					  proc->pid, thread->pid,
					  (u64)object_offset,
					  (u64)off_min,
					  (u64)t->buffer->data_size);
			return_error = BR_FAILED_REPLY;
@@ -3241,8 +3292,9 @@ static void binder_transaction(struct binder_proc *proc,
			goto err_bad_offset;
		}

		hdr = (struct binder_object_header *)(t->buffer->data + *offp);
		off_min = *offp + object_size;
		hdr = (struct binder_object_header *)
			(t->buffer->data + object_offset);
		off_min = object_offset + object_size;
		switch (hdr->type) {
		case BINDER_TYPE_BINDER:
		case BINDER_TYPE_WEAK_BINDER: {
+59 −0
Original line number Diff line number Diff line
@@ -1131,3 +1131,62 @@ binder_alloc_copy_user_to_buffer(struct binder_alloc *alloc,
	}
	return 0;
}

static void binder_alloc_do_buffer_copy(struct binder_alloc *alloc,
					bool to_buffer,
					struct binder_buffer *buffer,
					binder_size_t buffer_offset,
					void *ptr,
					size_t bytes)
{
	/* All copies must be 32-bit aligned and 32-bit size */
	BUG_ON(!check_buffer(alloc, buffer, buffer_offset, bytes));

	while (bytes) {
		unsigned long size;
		struct page *page;
		pgoff_t pgoff;
		void *tmpptr;
		void *base_ptr;

		page = binder_alloc_get_page(alloc, buffer,
					     buffer_offset, &pgoff);
		size = min_t(size_t, bytes, PAGE_SIZE - pgoff);
		base_ptr = kmap_atomic(page);
		tmpptr = base_ptr + pgoff;
		if (to_buffer)
			memcpy(tmpptr, ptr, size);
		else
			memcpy(ptr, tmpptr, size);
		/*
		 * kunmap_atomic() takes care of flushing the cache
		 * if this device has VIVT cache arch
		 */
		kunmap_atomic(base_ptr);
		bytes -= size;
		pgoff = 0;
		ptr = ptr + size;
		buffer_offset += size;
	}
}

void binder_alloc_copy_to_buffer(struct binder_alloc *alloc,
				 struct binder_buffer *buffer,
				 binder_size_t buffer_offset,
				 void *src,
				 size_t bytes)
{
	binder_alloc_do_buffer_copy(alloc, true, buffer, buffer_offset,
				    src, bytes);
}

void binder_alloc_copy_from_buffer(struct binder_alloc *alloc,
				   void *dest,
				   struct binder_buffer *buffer,
				   binder_size_t buffer_offset,
				   size_t bytes)
{
	binder_alloc_do_buffer_copy(alloc, false, buffer, buffer_offset,
				    dest, bytes);
}
+12 −0
Original line number Diff line number Diff line
@@ -191,5 +191,17 @@ binder_alloc_copy_user_to_buffer(struct binder_alloc *alloc,
				 const void __user *from,
				 size_t bytes);

void binder_alloc_copy_to_buffer(struct binder_alloc *alloc,
				 struct binder_buffer *buffer,
				 binder_size_t buffer_offset,
				 void *src,
				 size_t bytes);

void binder_alloc_copy_from_buffer(struct binder_alloc *alloc,
				   void *dest,
				   struct binder_buffer *buffer,
				   binder_size_t buffer_offset,
				   size_t bytes);

#endif /* _LINUX_BINDER_ALLOC_H */