]>
Commit | Line | Data |
---|---|---|
6ee3c412 GKH |
1 | From 84652aefb347297aa08e91e283adf7b18f77c2d5 Mon Sep 17 00:00:00 2001 |
2 | From: Roland Dreier <roland@purestorage.com> | |
3 | Date: Wed, 28 Mar 2018 11:27:22 -0700 | |
4 | Subject: RDMA/ucma: Introduce safer rdma_addr_size() variants | |
5 | ||
6 | From: Roland Dreier <roland@purestorage.com> | |
7 | ||
8 | commit 84652aefb347297aa08e91e283adf7b18f77c2d5 upstream. | |
9 | ||
10 | There are several places in the ucma ABI where userspace can pass in a | |
11 | sockaddr but set the address family to AF_IB. When that happens, | |
12 | rdma_addr_size() will return a size bigger than sizeof struct sockaddr_in6, | |
13 | and the ucma kernel code might end up copying past the end of a buffer | |
14 | not sized for a struct sockaddr_ib. | |
15 | ||
16 | Fix this by introducing new variants | |
17 | ||
18 | int rdma_addr_size_in6(struct sockaddr_in6 *addr); | |
19 | int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr); | |
20 | ||
21 | that are type-safe for the types used in the ucma ABI and return 0 if the | |
22 | size computed is bigger than the size of the type passed in. We can use | |
23 | these new variants to check what size userspace has passed in before | |
24 | copying any addresses. | |
25 | ||
26 | Reported-by: <syzbot+6800425d54ed3ed8135d@syzkaller.appspotmail.com> | |
27 | Signed-off-by: Roland Dreier <roland@purestorage.com> | |
28 | Signed-off-by: Jason Gunthorpe <jgg@mellanox.com> | |
29 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
30 | ||
31 | --- | |
32 | drivers/infiniband/core/addr.c | 16 ++++++++++++++++ | |
33 | drivers/infiniband/core/ucma.c | 34 +++++++++++++++++----------------- | |
34 | include/rdma/ib_addr.h | 2 ++ | |
35 | 3 files changed, 35 insertions(+), 17 deletions(-) | |
36 | ||
37 | --- a/drivers/infiniband/core/addr.c | |
38 | +++ b/drivers/infiniband/core/addr.c | |
39 | @@ -207,6 +207,22 @@ int rdma_addr_size(struct sockaddr *addr | |
40 | } | |
41 | EXPORT_SYMBOL(rdma_addr_size); | |
42 | ||
43 | +int rdma_addr_size_in6(struct sockaddr_in6 *addr) | |
44 | +{ | |
45 | + int ret = rdma_addr_size((struct sockaddr *) addr); | |
46 | + | |
47 | + return ret <= sizeof(*addr) ? ret : 0; | |
48 | +} | |
49 | +EXPORT_SYMBOL(rdma_addr_size_in6); | |
50 | + | |
51 | +int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr) | |
52 | +{ | |
53 | + int ret = rdma_addr_size((struct sockaddr *) addr); | |
54 | + | |
55 | + return ret <= sizeof(*addr) ? ret : 0; | |
56 | +} | |
57 | +EXPORT_SYMBOL(rdma_addr_size_kss); | |
58 | + | |
59 | static struct rdma_addr_client self; | |
60 | ||
61 | void rdma_addr_register_client(struct rdma_addr_client *client) | |
62 | --- a/drivers/infiniband/core/ucma.c | |
63 | +++ b/drivers/infiniband/core/ucma.c | |
64 | @@ -632,6 +632,9 @@ static ssize_t ucma_bind_ip(struct ucma_ | |
65 | if (copy_from_user(&cmd, inbuf, sizeof(cmd))) | |
66 | return -EFAULT; | |
67 | ||
68 | + if (!rdma_addr_size_in6(&cmd.addr)) | |
69 | + return -EINVAL; | |
70 | + | |
71 | ctx = ucma_get_ctx(file, cmd.id); | |
72 | if (IS_ERR(ctx)) | |
73 | return PTR_ERR(ctx); | |
74 | @@ -645,22 +648,21 @@ static ssize_t ucma_bind(struct ucma_fil | |
75 | int in_len, int out_len) | |
76 | { | |
77 | struct rdma_ucm_bind cmd; | |
78 | - struct sockaddr *addr; | |
79 | struct ucma_context *ctx; | |
80 | int ret; | |
81 | ||
82 | if (copy_from_user(&cmd, inbuf, sizeof(cmd))) | |
83 | return -EFAULT; | |
84 | ||
85 | - addr = (struct sockaddr *) &cmd.addr; | |
86 | - if (cmd.reserved || !cmd.addr_size || (cmd.addr_size != rdma_addr_size(addr))) | |
87 | + if (cmd.reserved || !cmd.addr_size || | |
88 | + cmd.addr_size != rdma_addr_size_kss(&cmd.addr)) | |
89 | return -EINVAL; | |
90 | ||
91 | ctx = ucma_get_ctx(file, cmd.id); | |
92 | if (IS_ERR(ctx)) | |
93 | return PTR_ERR(ctx); | |
94 | ||
95 | - ret = rdma_bind_addr(ctx->cm_id, addr); | |
96 | + ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr); | |
97 | ucma_put_ctx(ctx); | |
98 | return ret; | |
99 | } | |
100 | @@ -670,23 +672,22 @@ static ssize_t ucma_resolve_ip(struct uc | |
101 | int in_len, int out_len) | |
102 | { | |
103 | struct rdma_ucm_resolve_ip cmd; | |
104 | - struct sockaddr *src, *dst; | |
105 | struct ucma_context *ctx; | |
106 | int ret; | |
107 | ||
108 | if (copy_from_user(&cmd, inbuf, sizeof(cmd))) | |
109 | return -EFAULT; | |
110 | ||
111 | - src = (struct sockaddr *) &cmd.src_addr; | |
112 | - dst = (struct sockaddr *) &cmd.dst_addr; | |
113 | - if (!rdma_addr_size(src) || !rdma_addr_size(dst)) | |
114 | + if (!rdma_addr_size_in6(&cmd.src_addr) || | |
115 | + !rdma_addr_size_in6(&cmd.dst_addr)) | |
116 | return -EINVAL; | |
117 | ||
118 | ctx = ucma_get_ctx(file, cmd.id); | |
119 | if (IS_ERR(ctx)) | |
120 | return PTR_ERR(ctx); | |
121 | ||
122 | - ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms); | |
123 | + ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr, | |
124 | + (struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms); | |
125 | ucma_put_ctx(ctx); | |
126 | return ret; | |
127 | } | |
128 | @@ -696,24 +697,23 @@ static ssize_t ucma_resolve_addr(struct | |
129 | int in_len, int out_len) | |
130 | { | |
131 | struct rdma_ucm_resolve_addr cmd; | |
132 | - struct sockaddr *src, *dst; | |
133 | struct ucma_context *ctx; | |
134 | int ret; | |
135 | ||
136 | if (copy_from_user(&cmd, inbuf, sizeof(cmd))) | |
137 | return -EFAULT; | |
138 | ||
139 | - src = (struct sockaddr *) &cmd.src_addr; | |
140 | - dst = (struct sockaddr *) &cmd.dst_addr; | |
141 | - if (cmd.reserved || (cmd.src_size && (cmd.src_size != rdma_addr_size(src))) || | |
142 | - !cmd.dst_size || (cmd.dst_size != rdma_addr_size(dst))) | |
143 | + if (cmd.reserved || | |
144 | + (cmd.src_size && (cmd.src_size != rdma_addr_size_kss(&cmd.src_addr))) || | |
145 | + !cmd.dst_size || (cmd.dst_size != rdma_addr_size_kss(&cmd.dst_addr))) | |
146 | return -EINVAL; | |
147 | ||
148 | ctx = ucma_get_ctx(file, cmd.id); | |
149 | if (IS_ERR(ctx)) | |
150 | return PTR_ERR(ctx); | |
151 | ||
152 | - ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms); | |
153 | + ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr, | |
154 | + (struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms); | |
155 | ucma_put_ctx(ctx); | |
156 | return ret; | |
157 | } | |
158 | @@ -1432,7 +1432,7 @@ static ssize_t ucma_join_ip_multicast(st | |
159 | join_cmd.response = cmd.response; | |
160 | join_cmd.uid = cmd.uid; | |
161 | join_cmd.id = cmd.id; | |
162 | - join_cmd.addr_size = rdma_addr_size((struct sockaddr *) &cmd.addr); | |
163 | + join_cmd.addr_size = rdma_addr_size_in6(&cmd.addr); | |
164 | if (!join_cmd.addr_size) | |
165 | return -EINVAL; | |
166 | ||
167 | @@ -1451,7 +1451,7 @@ static ssize_t ucma_join_multicast(struc | |
168 | if (copy_from_user(&cmd, inbuf, sizeof(cmd))) | |
169 | return -EFAULT; | |
170 | ||
171 | - if (!rdma_addr_size((struct sockaddr *)&cmd.addr)) | |
172 | + if (!rdma_addr_size_kss(&cmd.addr)) | |
173 | return -EINVAL; | |
174 | ||
175 | return ucma_process_join(file, &cmd, out_len); | |
176 | --- a/include/rdma/ib_addr.h | |
177 | +++ b/include/rdma/ib_addr.h | |
178 | @@ -129,6 +129,8 @@ int rdma_copy_addr(struct rdma_dev_addr | |
179 | const unsigned char *dst_dev_addr); | |
180 | ||
181 | int rdma_addr_size(struct sockaddr *addr); | |
182 | +int rdma_addr_size_in6(struct sockaddr_in6 *addr); | |
183 | +int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr); | |
184 | ||
185 | int rdma_addr_find_smac_by_sgid(union ib_gid *sgid, u8 *smac, u16 *vlan_id); | |
186 | int rdma_addr_find_l2_eth_by_grh(const union ib_gid *sgid, |