Skip to content

Commit 1a7c3d9

Browse files
Todd Kjosgregkh
authored andcommitted
binder: create userspace-to-binder-buffer copy function
The binder driver uses a vm_area to map the per-process binder buffer space. For 32-bit android devices, this is now taking too much vmalloc space. This patch removes the use of vm_area when copying the transaction data from the sender to the buffer space. Instead of using copy_from_user() for multi-page copies, it now uses binder_alloc_copy_user_to_buffer() which uses kmap() and kunmap() to map each page, and uses copy_from_user() for copying to that page. Signed-off-by: Todd Kjos <tkjos@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 6cffd79 commit 1a7c3d9

File tree

3 files changed

+143
-7
lines changed

3 files changed

+143
-7
lines changed

drivers/android/binder.c

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3078,17 +3078,26 @@ static void binder_transaction(struct binder_proc *proc,
30783078
ALIGN(tr->data_size, sizeof(void *)));
30793079
offp = off_start;
30803080

3081-
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
3082-
tr->data.ptr.buffer, tr->data_size)) {
3081+
if (binder_alloc_copy_user_to_buffer(
3082+
&target_proc->alloc,
3083+
t->buffer, 0,
3084+
(const void __user *)
3085+
(uintptr_t)tr->data.ptr.buffer,
3086+
tr->data_size)) {
30833087
binder_user_error("%d:%d got transaction with invalid data ptr\n",
30843088
proc->pid, thread->pid);
30853089
return_error = BR_FAILED_REPLY;
30863090
return_error_param = -EFAULT;
30873091
return_error_line = __LINE__;
30883092
goto err_copy_data_failed;
30893093
}
3090-
if (copy_from_user(offp, (const void __user *)(uintptr_t)
3091-
tr->data.ptr.offsets, tr->offsets_size)) {
3094+
if (binder_alloc_copy_user_to_buffer(
3095+
&target_proc->alloc,
3096+
t->buffer,
3097+
ALIGN(tr->data_size, sizeof(void *)),
3098+
(const void __user *)
3099+
(uintptr_t)tr->data.ptr.offsets,
3100+
tr->offsets_size)) {
30923101
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
30933102
proc->pid, thread->pid);
30943103
return_error = BR_FAILED_REPLY;
@@ -3217,6 +3226,8 @@ static void binder_transaction(struct binder_proc *proc,
32173226
struct binder_buffer_object *bp =
32183227
to_binder_buffer_object(hdr);
32193228
size_t buf_left = sg_buf_end - sg_bufp;
3229+
binder_size_t sg_buf_offset = (uintptr_t)sg_bufp -
3230+
(uintptr_t)t->buffer->data;
32203231

32213232
if (bp->length > buf_left) {
32223233
binder_user_error("%d:%d got transaction with too large buffer\n",
@@ -3226,9 +3237,13 @@ static void binder_transaction(struct binder_proc *proc,
32263237
return_error_line = __LINE__;
32273238
goto err_bad_offset;
32283239
}
3229-
if (copy_from_user(sg_bufp,
3230-
(const void __user *)(uintptr_t)
3231-
bp->buffer, bp->length)) {
3240+
if (binder_alloc_copy_user_to_buffer(
3241+
&target_proc->alloc,
3242+
t->buffer,
3243+
sg_buf_offset,
3244+
(const void __user *)
3245+
(uintptr_t)bp->buffer,
3246+
bp->length)) {
32323247
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
32333248
proc->pid, thread->pid);
32343249
return_error_param = -EFAULT;

drivers/android/binder_alloc.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include <linux/list_lru.h>
3030
#include <linux/ratelimit.h>
3131
#include <asm/cacheflush.h>
32+
#include <linux/uaccess.h>
33+
#include <linux/highmem.h>
3234
#include "binder_alloc.h"
3335
#include "binder_trace.h"
3436

@@ -1053,3 +1055,114 @@ int binder_alloc_shrinker_init(void)
10531055
}
10541056
return ret;
10551057
}
1058+
1059+
/**
1060+
* check_buffer() - verify that buffer/offset is safe to access
1061+
* @alloc: binder_alloc for this proc
1062+
* @buffer: binder buffer to be accessed
1063+
* @offset: offset into @buffer data
1064+
* @bytes: bytes to access from offset
1065+
*
1066+
* Check that the @offset/@bytes are within the size of the given
1067+
* @buffer and that the buffer is currently active and not freeable.
1068+
* Offsets must also be multiples of sizeof(u32). The kernel is
1069+
* allowed to touch the buffer in two cases:
1070+
*
1071+
* 1) when the buffer is being created:
1072+
* (buffer->free == 0 && buffer->allow_user_free == 0)
1073+
* 2) when the buffer is being torn down:
1074+
* (buffer->free == 0 && buffer->transaction == NULL).
1075+
*
1076+
* Return: true if the buffer is safe to access
1077+
*/
1078+
static inline bool check_buffer(struct binder_alloc *alloc,
1079+
struct binder_buffer *buffer,
1080+
binder_size_t offset, size_t bytes)
1081+
{
1082+
size_t buffer_size = binder_alloc_buffer_size(alloc, buffer);
1083+
1084+
return buffer_size >= bytes &&
1085+
offset <= buffer_size - bytes &&
1086+
IS_ALIGNED(offset, sizeof(u32)) &&
1087+
!buffer->free &&
1088+
(!buffer->allow_user_free || !buffer->transaction);
1089+
}
1090+
1091+
/**
1092+
* binder_alloc_get_page() - get kernel pointer for given buffer offset
1093+
* @alloc: binder_alloc for this proc
1094+
* @buffer: binder buffer to be accessed
1095+
* @buffer_offset: offset into @buffer data
1096+
* @pgoffp: address to copy final page offset to
1097+
*
1098+
* Lookup the struct page corresponding to the address
1099+
* at @buffer_offset into @buffer->data. If @pgoffp is not
1100+
* NULL, the byte-offset into the page is written there.
1101+
*
1102+
* The caller is responsible to ensure that the offset points
1103+
* to a valid address within the @buffer and that @buffer is
1104+
* not freeable by the user. Since it can't be freed, we are
1105+
* guaranteed that the corresponding elements of @alloc->pages[]
1106+
* cannot change.
1107+
*
1108+
* Return: struct page
1109+
*/
1110+
static struct page *binder_alloc_get_page(struct binder_alloc *alloc,
1111+
struct binder_buffer *buffer,
1112+
binder_size_t buffer_offset,
1113+
pgoff_t *pgoffp)
1114+
{
1115+
binder_size_t buffer_space_offset = buffer_offset +
1116+
(buffer->data - alloc->buffer);
1117+
pgoff_t pgoff = buffer_space_offset & ~PAGE_MASK;
1118+
size_t index = buffer_space_offset >> PAGE_SHIFT;
1119+
struct binder_lru_page *lru_page;
1120+
1121+
lru_page = &alloc->pages[index];
1122+
*pgoffp = pgoff;
1123+
return lru_page->page_ptr;
1124+
}
1125+
1126+
/**
1127+
* binder_alloc_copy_user_to_buffer() - copy src user to tgt user
1128+
* @alloc: binder_alloc for this proc
1129+
* @buffer: binder buffer to be accessed
1130+
* @buffer_offset: offset into @buffer data
1131+
* @from: userspace pointer to source buffer
1132+
* @bytes: bytes to copy
1133+
*
1134+
* Copy bytes from source userspace to target buffer.
1135+
*
1136+
* Return: bytes remaining to be copied
1137+
*/
1138+
unsigned long
1139+
binder_alloc_copy_user_to_buffer(struct binder_alloc *alloc,
1140+
struct binder_buffer *buffer,
1141+
binder_size_t buffer_offset,
1142+
const void __user *from,
1143+
size_t bytes)
1144+
{
1145+
if (!check_buffer(alloc, buffer, buffer_offset, bytes))
1146+
return bytes;
1147+
1148+
while (bytes) {
1149+
unsigned long size;
1150+
unsigned long ret;
1151+
struct page *page;
1152+
pgoff_t pgoff;
1153+
void *kptr;
1154+
1155+
page = binder_alloc_get_page(alloc, buffer,
1156+
buffer_offset, &pgoff);
1157+
size = min_t(size_t, bytes, PAGE_SIZE - pgoff);
1158+
kptr = kmap(page) + pgoff;
1159+
ret = copy_from_user(kptr, from, size);
1160+
kunmap(page);
1161+
if (ret)
1162+
return bytes - size + ret;
1163+
bytes -= size;
1164+
from += size;
1165+
buffer_offset += size;
1166+
}
1167+
return 0;
1168+
}

drivers/android/binder_alloc.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <linux/vmalloc.h>
2323
#include <linux/slab.h>
2424
#include <linux/list_lru.h>
25+
#include <uapi/linux/android/binder.h>
2526

2627
extern struct list_lru binder_alloc_lru;
2728
struct binder_transaction;
@@ -183,5 +184,12 @@ binder_alloc_get_user_buffer_offset(struct binder_alloc *alloc)
183184
return alloc->user_buffer_offset;
184185
}
185186

187+
unsigned long
188+
binder_alloc_copy_user_to_buffer(struct binder_alloc *alloc,
189+
struct binder_buffer *buffer,
190+
binder_size_t buffer_offset,
191+
const void __user *from,
192+
size_t bytes);
193+
186194
#endif /* _LINUX_BINDER_ALLOC_H */
187195

0 commit comments

Comments
 (0)