Commit 84652aef authored by Roland Dreier's avatar Roland Dreier Committed by Jason Gunthorpe

RDMA/ucma: Introduce safer rdma_addr_size() variants

There are several places in the ucma ABI where userspace can pass in a
sockaddr but set the address family to AF_IB.  When that happens,
rdma_addr_size() will return a size bigger than sizeof struct sockaddr_in6,
and the ucma kernel code might end up copying past the end of a buffer
not sized for a struct sockaddr_ib.

Fix this by introducing new variants

    int rdma_addr_size_in6(struct sockaddr_in6 *addr);
    int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr);

that are type-safe for the types used in the ucma ABI and return 0 if the
size computed is bigger than the size of the type passed in.  We can use
these new variants to check what size userspace has passed in before
copying any addresses.

Reported-by: <syzbot+6800425d54ed3ed8135d@syzkaller.appspotmail.com>
Signed-off-by: default avatarRoland Dreier <roland@purestorage.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
parent 38759d61
...@@ -207,6 +207,22 @@ int rdma_addr_size(struct sockaddr *addr) ...@@ -207,6 +207,22 @@ int rdma_addr_size(struct sockaddr *addr)
} }
EXPORT_SYMBOL(rdma_addr_size); EXPORT_SYMBOL(rdma_addr_size);
int rdma_addr_size_in6(struct sockaddr_in6 *addr)
{
int ret = rdma_addr_size((struct sockaddr *) addr);
return ret <= sizeof(*addr) ? ret : 0;
}
EXPORT_SYMBOL(rdma_addr_size_in6);
int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr)
{
int ret = rdma_addr_size((struct sockaddr *) addr);
return ret <= sizeof(*addr) ? ret : 0;
}
EXPORT_SYMBOL(rdma_addr_size_kss);
static struct rdma_addr_client self; static struct rdma_addr_client self;
void rdma_addr_register_client(struct rdma_addr_client *client) void rdma_addr_register_client(struct rdma_addr_client *client)
......
...@@ -632,6 +632,9 @@ static ssize_t ucma_bind_ip(struct ucma_file *file, const char __user *inbuf, ...@@ -632,6 +632,9 @@ static ssize_t ucma_bind_ip(struct ucma_file *file, const char __user *inbuf,
if (copy_from_user(&cmd, inbuf, sizeof(cmd))) if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT; return -EFAULT;
if (!rdma_addr_size_in6(&cmd.addr))
return -EINVAL;
ctx = ucma_get_ctx(file, cmd.id); ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx)) if (IS_ERR(ctx))
return PTR_ERR(ctx); return PTR_ERR(ctx);
...@@ -645,22 +648,21 @@ static ssize_t ucma_bind(struct ucma_file *file, const char __user *inbuf, ...@@ -645,22 +648,21 @@ static ssize_t ucma_bind(struct ucma_file *file, const char __user *inbuf,
int in_len, int out_len) int in_len, int out_len)
{ {
struct rdma_ucm_bind cmd; struct rdma_ucm_bind cmd;
struct sockaddr *addr;
struct ucma_context *ctx; struct ucma_context *ctx;
int ret; int ret;
if (copy_from_user(&cmd, inbuf, sizeof(cmd))) if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT; return -EFAULT;
addr = (struct sockaddr *) &cmd.addr; if (cmd.reserved || !cmd.addr_size ||
if (cmd.reserved || !cmd.addr_size || (cmd.addr_size != rdma_addr_size(addr))) cmd.addr_size != rdma_addr_size_kss(&cmd.addr))
return -EINVAL; return -EINVAL;
ctx = ucma_get_ctx(file, cmd.id); ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx)) if (IS_ERR(ctx))
return PTR_ERR(ctx); return PTR_ERR(ctx);
ret = rdma_bind_addr(ctx->cm_id, addr); ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr);
ucma_put_ctx(ctx); ucma_put_ctx(ctx);
return ret; return ret;
} }
...@@ -670,23 +672,22 @@ static ssize_t ucma_resolve_ip(struct ucma_file *file, ...@@ -670,23 +672,22 @@ static ssize_t ucma_resolve_ip(struct ucma_file *file,
int in_len, int out_len) int in_len, int out_len)
{ {
struct rdma_ucm_resolve_ip cmd; struct rdma_ucm_resolve_ip cmd;
struct sockaddr *src, *dst;
struct ucma_context *ctx; struct ucma_context *ctx;
int ret; int ret;
if (copy_from_user(&cmd, inbuf, sizeof(cmd))) if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT; return -EFAULT;
src = (struct sockaddr *) &cmd.src_addr; if (!rdma_addr_size_in6(&cmd.src_addr) ||
dst = (struct sockaddr *) &cmd.dst_addr; !rdma_addr_size_in6(&cmd.dst_addr))
if (!rdma_addr_size(src) || !rdma_addr_size(dst))
return -EINVAL; return -EINVAL;
ctx = ucma_get_ctx(file, cmd.id); ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx)) if (IS_ERR(ctx))
return PTR_ERR(ctx); return PTR_ERR(ctx);
ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms); ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr,
(struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms);
ucma_put_ctx(ctx); ucma_put_ctx(ctx);
return ret; return ret;
} }
...@@ -696,24 +697,23 @@ static ssize_t ucma_resolve_addr(struct ucma_file *file, ...@@ -696,24 +697,23 @@ static ssize_t ucma_resolve_addr(struct ucma_file *file,
int in_len, int out_len) int in_len, int out_len)
{ {
struct rdma_ucm_resolve_addr cmd; struct rdma_ucm_resolve_addr cmd;
struct sockaddr *src, *dst;
struct ucma_context *ctx; struct ucma_context *ctx;
int ret; int ret;
if (copy_from_user(&cmd, inbuf, sizeof(cmd))) if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT; return -EFAULT;
src = (struct sockaddr *) &cmd.src_addr; if (cmd.reserved ||
dst = (struct sockaddr *) &cmd.dst_addr; (cmd.src_size && (cmd.src_size != rdma_addr_size_kss(&cmd.src_addr))) ||
if (cmd.reserved || (cmd.src_size && (cmd.src_size != rdma_addr_size(src))) || !cmd.dst_size || (cmd.dst_size != rdma_addr_size_kss(&cmd.dst_addr)))
!cmd.dst_size || (cmd.dst_size != rdma_addr_size(dst)))
return -EINVAL; return -EINVAL;
ctx = ucma_get_ctx(file, cmd.id); ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx)) if (IS_ERR(ctx))
return PTR_ERR(ctx); return PTR_ERR(ctx);
ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms); ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr,
(struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms);
ucma_put_ctx(ctx); ucma_put_ctx(ctx);
return ret; return ret;
} }
...@@ -1433,7 +1433,7 @@ static ssize_t ucma_join_ip_multicast(struct ucma_file *file, ...@@ -1433,7 +1433,7 @@ static ssize_t ucma_join_ip_multicast(struct ucma_file *file,
join_cmd.response = cmd.response; join_cmd.response = cmd.response;
join_cmd.uid = cmd.uid; join_cmd.uid = cmd.uid;
join_cmd.id = cmd.id; join_cmd.id = cmd.id;
join_cmd.addr_size = rdma_addr_size((struct sockaddr *) &cmd.addr); join_cmd.addr_size = rdma_addr_size_in6(&cmd.addr);
if (!join_cmd.addr_size) if (!join_cmd.addr_size)
return -EINVAL; return -EINVAL;
...@@ -1452,7 +1452,7 @@ static ssize_t ucma_join_multicast(struct ucma_file *file, ...@@ -1452,7 +1452,7 @@ static ssize_t ucma_join_multicast(struct ucma_file *file,
if (copy_from_user(&cmd, inbuf, sizeof(cmd))) if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT; return -EFAULT;
if (!rdma_addr_size((struct sockaddr *)&cmd.addr)) if (!rdma_addr_size_kss(&cmd.addr))
return -EINVAL; return -EINVAL;
return ucma_process_join(file, &cmd, out_len); return ucma_process_join(file, &cmd, out_len);
......
...@@ -130,6 +130,8 @@ void rdma_copy_addr(struct rdma_dev_addr *dev_addr, ...@@ -130,6 +130,8 @@ void rdma_copy_addr(struct rdma_dev_addr *dev_addr,
const unsigned char *dst_dev_addr); const unsigned char *dst_dev_addr);
int rdma_addr_size(struct sockaddr *addr); int rdma_addr_size(struct sockaddr *addr);
int rdma_addr_size_in6(struct sockaddr_in6 *addr);
int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr);
int rdma_addr_find_l2_eth_by_grh(const union ib_gid *sgid, int rdma_addr_find_l2_eth_by_grh(const union ib_gid *sgid,
const union ib_gid *dgid, const union ib_gid *dgid,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment