Skip to content

Commit 84652ae

Browse files
rolanddjgunthorpe
authored andcommitted
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: <[email protected]> Signed-off-by: Roland Dreier <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent 38759d6 commit 84652ae

File tree

3 files changed

+35
-17
lines changed

3 files changed

+35
-17
lines changed

drivers/infiniband/core/addr.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,22 @@ int rdma_addr_size(struct sockaddr *addr)
207207
}
208208
EXPORT_SYMBOL(rdma_addr_size);
209209

210+
int rdma_addr_size_in6(struct sockaddr_in6 *addr)
211+
{
212+
int ret = rdma_addr_size((struct sockaddr *) addr);
213+
214+
return ret <= sizeof(*addr) ? ret : 0;
215+
}
216+
EXPORT_SYMBOL(rdma_addr_size_in6);
217+
218+
int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr)
219+
{
220+
int ret = rdma_addr_size((struct sockaddr *) addr);
221+
222+
return ret <= sizeof(*addr) ? ret : 0;
223+
}
224+
EXPORT_SYMBOL(rdma_addr_size_kss);
225+
210226
static struct rdma_addr_client self;
211227

212228
void rdma_addr_register_client(struct rdma_addr_client *client)

drivers/infiniband/core/ucma.c

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,9 @@ static ssize_t ucma_bind_ip(struct ucma_file *file, const char __user *inbuf,
632632
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
633633
return -EFAULT;
634634

635+
if (!rdma_addr_size_in6(&cmd.addr))
636+
return -EINVAL;
637+
635638
ctx = ucma_get_ctx(file, cmd.id);
636639
if (IS_ERR(ctx))
637640
return PTR_ERR(ctx);
@@ -645,22 +648,21 @@ static ssize_t ucma_bind(struct ucma_file *file, const char __user *inbuf,
645648
int in_len, int out_len)
646649
{
647650
struct rdma_ucm_bind cmd;
648-
struct sockaddr *addr;
649651
struct ucma_context *ctx;
650652
int ret;
651653

652654
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
653655
return -EFAULT;
654656

655-
addr = (struct sockaddr *) &cmd.addr;
656-
if (cmd.reserved || !cmd.addr_size || (cmd.addr_size != rdma_addr_size(addr)))
657+
if (cmd.reserved || !cmd.addr_size ||
658+
cmd.addr_size != rdma_addr_size_kss(&cmd.addr))
657659
return -EINVAL;
658660

659661
ctx = ucma_get_ctx(file, cmd.id);
660662
if (IS_ERR(ctx))
661663
return PTR_ERR(ctx);
662664

663-
ret = rdma_bind_addr(ctx->cm_id, addr);
665+
ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr);
664666
ucma_put_ctx(ctx);
665667
return ret;
666668
}
@@ -670,23 +672,22 @@ static ssize_t ucma_resolve_ip(struct ucma_file *file,
670672
int in_len, int out_len)
671673
{
672674
struct rdma_ucm_resolve_ip cmd;
673-
struct sockaddr *src, *dst;
674675
struct ucma_context *ctx;
675676
int ret;
676677

677678
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
678679
return -EFAULT;
679680

680-
src = (struct sockaddr *) &cmd.src_addr;
681-
dst = (struct sockaddr *) &cmd.dst_addr;
682-
if (!rdma_addr_size(src) || !rdma_addr_size(dst))
681+
if (!rdma_addr_size_in6(&cmd.src_addr) ||
682+
!rdma_addr_size_in6(&cmd.dst_addr))
683683
return -EINVAL;
684684

685685
ctx = ucma_get_ctx(file, cmd.id);
686686
if (IS_ERR(ctx))
687687
return PTR_ERR(ctx);
688688

689-
ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms);
689+
ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr,
690+
(struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms);
690691
ucma_put_ctx(ctx);
691692
return ret;
692693
}
@@ -696,24 +697,23 @@ static ssize_t ucma_resolve_addr(struct ucma_file *file,
696697
int in_len, int out_len)
697698
{
698699
struct rdma_ucm_resolve_addr cmd;
699-
struct sockaddr *src, *dst;
700700
struct ucma_context *ctx;
701701
int ret;
702702

703703
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
704704
return -EFAULT;
705705

706-
src = (struct sockaddr *) &cmd.src_addr;
707-
dst = (struct sockaddr *) &cmd.dst_addr;
708-
if (cmd.reserved || (cmd.src_size && (cmd.src_size != rdma_addr_size(src))) ||
709-
!cmd.dst_size || (cmd.dst_size != rdma_addr_size(dst)))
706+
if (cmd.reserved ||
707+
(cmd.src_size && (cmd.src_size != rdma_addr_size_kss(&cmd.src_addr))) ||
708+
!cmd.dst_size || (cmd.dst_size != rdma_addr_size_kss(&cmd.dst_addr)))
710709
return -EINVAL;
711710

712711
ctx = ucma_get_ctx(file, cmd.id);
713712
if (IS_ERR(ctx))
714713
return PTR_ERR(ctx);
715714

716-
ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms);
715+
ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr,
716+
(struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms);
717717
ucma_put_ctx(ctx);
718718
return ret;
719719
}
@@ -1433,7 +1433,7 @@ static ssize_t ucma_join_ip_multicast(struct ucma_file *file,
14331433
join_cmd.response = cmd.response;
14341434
join_cmd.uid = cmd.uid;
14351435
join_cmd.id = cmd.id;
1436-
join_cmd.addr_size = rdma_addr_size((struct sockaddr *) &cmd.addr);
1436+
join_cmd.addr_size = rdma_addr_size_in6(&cmd.addr);
14371437
if (!join_cmd.addr_size)
14381438
return -EINVAL;
14391439

@@ -1452,7 +1452,7 @@ static ssize_t ucma_join_multicast(struct ucma_file *file,
14521452
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
14531453
return -EFAULT;
14541454

1455-
if (!rdma_addr_size((struct sockaddr *)&cmd.addr))
1455+
if (!rdma_addr_size_kss(&cmd.addr))
14561456
return -EINVAL;
14571457

14581458
return ucma_process_join(file, &cmd, out_len);

include/rdma/ib_addr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ void rdma_copy_addr(struct rdma_dev_addr *dev_addr,
130130
const unsigned char *dst_dev_addr);
131131

132132
int rdma_addr_size(struct sockaddr *addr);
133+
int rdma_addr_size_in6(struct sockaddr_in6 *addr);
134+
int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr);
133135

134136
int rdma_addr_find_l2_eth_by_grh(const union ib_gid *sgid,
135137
const union ib_gid *dgid,

0 commit comments

Comments
 (0)