BACKPORT: binder: add functions to copy to/from binder buffers
authorTodd Kjos <tkjos@android.com>
Fri, 8 Feb 2019 18:35:15 +0000 (10:35 -0800)
committerTodd Kjos <tkjos@google.com>
Mon, 25 Mar 2019 22:19:45 +0000 (15:19 -0700)
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: Todd Kjos <tkjos@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/android/binder.c
drivers/android/binder_alloc.c
drivers/android/binder_alloc.h

index 264b5aaa12dafc030c2df6301c291ba2c0b44a17..e91e26a325534ae98fa16026316805b8957b3f57 100644 (file)
@@ -2368,14 +2368,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: {
@@ -2469,8 +2477,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",
@@ -2699,11 +2719,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;
 
@@ -2713,8 +2743,17 @@ err_translate_fd_failed:
         * 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;
 }
 
@@ -3165,7 +3204,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;
        }
@@ -3227,11 +3268,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);
-
-               if (object_size == 0 || *offp < off_min) {
+               size_t object_size;
+               binder_size_t object_offset;
+               binder_size_t buffer_offset =
+                       (uintptr_t)offp - (uintptr_t)t->buffer->data;
+
+               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;
@@ -3240,8 +3291,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: {
index 248bce37c25211e49029fd16f84e784b3ccb66b4..abd02fa49f3e66f6ce2dd06f026a51a61cf5d649 100644 (file)
@@ -1155,3 +1155,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);
+}
+
index b37a3e3270034fc5a0d63694e45831c41f214a71..a9bec2e923f75b1644216a7c6db221e3fb718b99 100644 (file)
@@ -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 */