Commit def95c73567dfacb22900cd0c4f01caff39e4c9e
Committed by
Greg Kroah-Hartman
1 parent
7980240b6d
binder: Add support for file-descriptor arrays
This patch introduces a new binder_fd_array object, that allows us to support one or more file descriptors embedded in a buffer that is scatter-gathered. Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Martijn Coenen <maco@google.com> Cc: Arve Hjønnevåg <arve@android.com> Cc: Amit Pundir <amit.pundir@linaro.org> Cc: Serban Constantinescu <serban.constantinescu@arm.com> Cc: Dmitry Shmidt <dimitrysh@google.com> Cc: Rom Lemarchand <romlem@google.com> Cc: Android Kernel Team <kernel-team@android.com> Signed-off-by: Martijn Coenen <maco@google.com> Signed-off-by: John Stultz <john.stultz@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Showing 2 changed files with 165 additions and 0 deletions Side-by-side Diff
drivers/android/binder.c
... | ... | @@ -155,6 +155,9 @@ |
155 | 155 | #define to_binder_buffer_object(hdr) \ |
156 | 156 | container_of(hdr, struct binder_buffer_object, hdr) |
157 | 157 | |
158 | +#define to_binder_fd_array_object(hdr) \ | |
159 | + container_of(hdr, struct binder_fd_array_object, hdr) | |
160 | + | |
158 | 161 | enum binder_stat_types { |
159 | 162 | BINDER_STAT_PROC, |
160 | 163 | BINDER_STAT_THREAD, |
... | ... | @@ -1310,6 +1313,9 @@ |
1310 | 1313 | case BINDER_TYPE_PTR: |
1311 | 1314 | object_size = sizeof(struct binder_buffer_object); |
1312 | 1315 | break; |
1316 | + case BINDER_TYPE_FDA: | |
1317 | + object_size = sizeof(struct binder_fd_array_object); | |
1318 | + break; | |
1313 | 1319 | default: |
1314 | 1320 | return 0; |
1315 | 1321 | } |
... | ... | @@ -1503,6 +1509,47 @@ |
1503 | 1509 | * transaction buffer gets freed |
1504 | 1510 | */ |
1505 | 1511 | break; |
1512 | + case BINDER_TYPE_FDA: { | |
1513 | + struct binder_fd_array_object *fda; | |
1514 | + struct binder_buffer_object *parent; | |
1515 | + uintptr_t parent_buffer; | |
1516 | + u32 *fd_array; | |
1517 | + size_t fd_index; | |
1518 | + binder_size_t fd_buf_size; | |
1519 | + | |
1520 | + fda = to_binder_fd_array_object(hdr); | |
1521 | + parent = binder_validate_ptr(buffer, fda->parent, | |
1522 | + off_start, | |
1523 | + offp - off_start); | |
1524 | + if (!parent) { | |
1525 | + pr_err("transaction release %d bad parent offset", | |
1526 | + debug_id); | |
1527 | + continue; | |
1528 | + } | |
1529 | + /* | |
1530 | + * Since the parent was already fixed up, convert it | |
1531 | + * back to kernel address space to access it | |
1532 | + */ | |
1533 | + parent_buffer = parent->buffer - | |
1534 | + proc->user_buffer_offset; | |
1535 | + | |
1536 | + fd_buf_size = sizeof(u32) * fda->num_fds; | |
1537 | + if (fda->num_fds >= SIZE_MAX / sizeof(u32)) { | |
1538 | + pr_err("transaction release %d invalid number of fds (%lld)\n", | |
1539 | + debug_id, (u64)fda->num_fds); | |
1540 | + continue; | |
1541 | + } | |
1542 | + if (fd_buf_size > parent->length || | |
1543 | + fda->parent_offset > parent->length - fd_buf_size) { | |
1544 | + /* No space for all file descriptors here. */ | |
1545 | + pr_err("transaction release %d not enough space for %lld fds in buffer\n", | |
1546 | + debug_id, (u64)fda->num_fds); | |
1547 | + continue; | |
1548 | + } | |
1549 | + fd_array = (u32 *)(parent_buffer + fda->parent_offset); | |
1550 | + for (fd_index = 0; fd_index < fda->num_fds; fd_index++) | |
1551 | + task_close_fd(proc, fd_array[fd_index]); | |
1552 | + } break; | |
1506 | 1553 | default: |
1507 | 1554 | pr_err("transaction release %d bad object type %x\n", |
1508 | 1555 | debug_id, hdr->type); |
... | ... | @@ -1672,6 +1719,63 @@ |
1672 | 1719 | return ret; |
1673 | 1720 | } |
1674 | 1721 | |
1722 | +static int binder_translate_fd_array(struct binder_fd_array_object *fda, | |
1723 | + struct binder_buffer_object *parent, | |
1724 | + struct binder_transaction *t, | |
1725 | + struct binder_thread *thread, | |
1726 | + struct binder_transaction *in_reply_to) | |
1727 | +{ | |
1728 | + binder_size_t fdi, fd_buf_size, num_installed_fds; | |
1729 | + int target_fd; | |
1730 | + uintptr_t parent_buffer; | |
1731 | + u32 *fd_array; | |
1732 | + struct binder_proc *proc = thread->proc; | |
1733 | + struct binder_proc *target_proc = t->to_proc; | |
1734 | + | |
1735 | + fd_buf_size = sizeof(u32) * fda->num_fds; | |
1736 | + if (fda->num_fds >= SIZE_MAX / sizeof(u32)) { | |
1737 | + binder_user_error("%d:%d got transaction with invalid number of fds (%lld)\n", | |
1738 | + proc->pid, thread->pid, (u64)fda->num_fds); | |
1739 | + return -EINVAL; | |
1740 | + } | |
1741 | + if (fd_buf_size > parent->length || | |
1742 | + fda->parent_offset > parent->length - fd_buf_size) { | |
1743 | + /* No space for all file descriptors here. */ | |
1744 | + binder_user_error("%d:%d not enough space to store %lld fds in buffer\n", | |
1745 | + proc->pid, thread->pid, (u64)fda->num_fds); | |
1746 | + return -EINVAL; | |
1747 | + } | |
1748 | + /* | |
1749 | + * Since the parent was already fixed up, convert it | |
1750 | + * back to the kernel address space to access it | |
1751 | + */ | |
1752 | + parent_buffer = parent->buffer - target_proc->user_buffer_offset; | |
1753 | + fd_array = (u32 *)(parent_buffer + fda->parent_offset); | |
1754 | + if (!IS_ALIGNED((unsigned long)fd_array, sizeof(u32))) { | |
1755 | + binder_user_error("%d:%d parent offset not aligned correctly.\n", | |
1756 | + proc->pid, thread->pid); | |
1757 | + return -EINVAL; | |
1758 | + } | |
1759 | + for (fdi = 0; fdi < fda->num_fds; fdi++) { | |
1760 | + target_fd = binder_translate_fd(fd_array[fdi], t, thread, | |
1761 | + in_reply_to); | |
1762 | + if (target_fd < 0) | |
1763 | + goto err_translate_fd_failed; | |
1764 | + fd_array[fdi] = target_fd; | |
1765 | + } | |
1766 | + return 0; | |
1767 | + | |
1768 | +err_translate_fd_failed: | |
1769 | + /* | |
1770 | + * Failed to allocate fd or security error, free fds | |
1771 | + * installed so far. | |
1772 | + */ | |
1773 | + num_installed_fds = fdi; | |
1774 | + for (fdi = 0; fdi < num_installed_fds; fdi++) | |
1775 | + task_close_fd(target_proc, fd_array[fdi]); | |
1776 | + return target_fd; | |
1777 | +} | |
1778 | + | |
1675 | 1779 | static int binder_fixup_parent(struct binder_transaction *t, |
1676 | 1780 | struct binder_thread *thread, |
1677 | 1781 | struct binder_buffer_object *bp, |
... | ... | @@ -2000,6 +2104,38 @@ |
2000 | 2104 | fp->pad_binder = 0; |
2001 | 2105 | fp->fd = target_fd; |
2002 | 2106 | } break; |
2107 | + case BINDER_TYPE_FDA: { | |
2108 | + struct binder_fd_array_object *fda = | |
2109 | + to_binder_fd_array_object(hdr); | |
2110 | + struct binder_buffer_object *parent = | |
2111 | + binder_validate_ptr(t->buffer, fda->parent, | |
2112 | + off_start, | |
2113 | + offp - off_start); | |
2114 | + if (!parent) { | |
2115 | + binder_user_error("%d:%d got transaction with invalid parent offset or type\n", | |
2116 | + proc->pid, thread->pid); | |
2117 | + return_error = BR_FAILED_REPLY; | |
2118 | + goto err_bad_parent; | |
2119 | + } | |
2120 | + if (!binder_validate_fixup(t->buffer, off_start, | |
2121 | + parent, fda->parent_offset, | |
2122 | + last_fixup_obj, | |
2123 | + last_fixup_min_off)) { | |
2124 | + binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n", | |
2125 | + proc->pid, thread->pid); | |
2126 | + return_error = BR_FAILED_REPLY; | |
2127 | + goto err_bad_parent; | |
2128 | + } | |
2129 | + ret = binder_translate_fd_array(fda, parent, t, thread, | |
2130 | + in_reply_to); | |
2131 | + if (ret < 0) { | |
2132 | + return_error = BR_FAILED_REPLY; | |
2133 | + goto err_translate_failed; | |
2134 | + } | |
2135 | + last_fixup_obj = parent; | |
2136 | + last_fixup_min_off = | |
2137 | + fda->parent_offset + sizeof(u32) * fda->num_fds; | |
2138 | + } break; | |
2003 | 2139 | case BINDER_TYPE_PTR: { |
2004 | 2140 | struct binder_buffer_object *bp = |
2005 | 2141 | to_binder_buffer_object(hdr); |
... | ... | @@ -2070,6 +2206,7 @@ |
2070 | 2206 | err_translate_failed: |
2071 | 2207 | err_bad_object_type: |
2072 | 2208 | err_bad_offset: |
2209 | +err_bad_parent: | |
2073 | 2210 | err_copy_data_failed: |
2074 | 2211 | trace_binder_transaction_failed_buffer_release(t->buffer); |
2075 | 2212 | binder_transaction_buffer_release(target_proc, t->buffer, offp); |
include/uapi/linux/android/binder.h
... | ... | @@ -33,6 +33,7 @@ |
33 | 33 | BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE), |
34 | 34 | BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE), |
35 | 35 | BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE), |
36 | + BINDER_TYPE_FDA = B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE), | |
36 | 37 | BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE), |
37 | 38 | }; |
38 | 39 | |
... | ... | @@ -127,6 +128,33 @@ |
127 | 128 | |
128 | 129 | enum { |
129 | 130 | BINDER_BUFFER_FLAG_HAS_PARENT = 0x01, |
131 | +}; | |
132 | + | |
133 | +/* struct binder_fd_array_object - object describing an array of fds in a buffer | |
134 | + * @hdr: common header structure | |
135 | + * @num_fds: number of file descriptors in the buffer | |
136 | + * @parent: index in offset array to buffer holding the fd array | |
137 | + * @parent_offset: start offset of fd array in the buffer | |
138 | + * | |
139 | + * A binder_fd_array object represents an array of file | |
140 | + * descriptors embedded in a binder_buffer_object. It is | |
141 | + * different from a regular binder_buffer_object because it | |
142 | + * describes a list of file descriptors to fix up, not an opaque | |
143 | + * blob of memory, and hence the kernel needs to treat it differently. | |
144 | + * | |
145 | + * An example of how this would be used is with Android's | |
146 | + * native_handle_t object, which is a struct with a list of integers | |
147 | + * and a list of file descriptors. The native_handle_t struct itself | |
148 | + * will be represented by a struct binder_buffer_objct, whereas the | |
149 | + * embedded list of file descriptors is represented by a | |
150 | + * struct binder_fd_array_object with that binder_buffer_object as | |
151 | + * a parent. | |
152 | + */ | |
153 | +struct binder_fd_array_object { | |
154 | + struct binder_object_header hdr; | |
155 | + binder_size_t num_fds; | |
156 | + binder_size_t parent; | |
157 | + binder_size_t parent_offset; | |
130 | 158 | }; |
131 | 159 | |
132 | 160 | /* |