Skip to content

Commit f9105c2

Browse files
rolanddgregkh
authored andcommitted
RDMA/ucma: Introduce safer rdma_addr_size() variants
commit 84652ae upstream. 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]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 71ac483 commit f9105c2

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
@@ -86,6 +86,22 @@ int rdma_addr_size(struct sockaddr *addr)
8686
}
8787
EXPORT_SYMBOL(rdma_addr_size);
8888

89+
int rdma_addr_size_in6(struct sockaddr_in6 *addr)
90+
{
91+
int ret = rdma_addr_size((struct sockaddr *) addr);
92+
93+
return ret <= sizeof(*addr) ? ret : 0;
94+
}
95+
EXPORT_SYMBOL(rdma_addr_size_in6);
96+
97+
int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr)
98+
{
99+
int ret = rdma_addr_size((struct sockaddr *) addr);
100+
101+
return ret <= sizeof(*addr) ? ret : 0;
102+
}
103+
EXPORT_SYMBOL(rdma_addr_size_kss);
104+
89105
static struct rdma_addr_client self;
90106

91107
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
@@ -629,6 +629,9 @@ static ssize_t ucma_bind_ip(struct ucma_file *file, const char __user *inbuf,
629629
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
630630
return -EFAULT;
631631

632+
if (!rdma_addr_size_in6(&cmd.addr))
633+
return -EINVAL;
634+
632635
ctx = ucma_get_ctx(file, cmd.id);
633636
if (IS_ERR(ctx))
634637
return PTR_ERR(ctx);
@@ -642,22 +645,21 @@ static ssize_t ucma_bind(struct ucma_file *file, const char __user *inbuf,
642645
int in_len, int out_len)
643646
{
644647
struct rdma_ucm_bind cmd;
645-
struct sockaddr *addr;
646648
struct ucma_context *ctx;
647649
int ret;
648650

649651
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
650652
return -EFAULT;
651653

652-
addr = (struct sockaddr *) &cmd.addr;
653-
if (cmd.reserved || !cmd.addr_size || (cmd.addr_size != rdma_addr_size(addr)))
654+
if (cmd.reserved || !cmd.addr_size ||
655+
cmd.addr_size != rdma_addr_size_kss(&cmd.addr))
654656
return -EINVAL;
655657

656658
ctx = ucma_get_ctx(file, cmd.id);
657659
if (IS_ERR(ctx))
658660
return PTR_ERR(ctx);
659661

660-
ret = rdma_bind_addr(ctx->cm_id, addr);
662+
ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr);
661663
ucma_put_ctx(ctx);
662664
return ret;
663665
}
@@ -667,23 +669,22 @@ static ssize_t ucma_resolve_ip(struct ucma_file *file,
667669
int in_len, int out_len)
668670
{
669671
struct rdma_ucm_resolve_ip cmd;
670-
struct sockaddr *src, *dst;
671672
struct ucma_context *ctx;
672673
int ret;
673674

674675
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
675676
return -EFAULT;
676677

677-
src = (struct sockaddr *) &cmd.src_addr;
678-
dst = (struct sockaddr *) &cmd.dst_addr;
679-
if (!rdma_addr_size(src) || !rdma_addr_size(dst))
678+
if (!rdma_addr_size_in6(&cmd.src_addr) ||
679+
!rdma_addr_size_in6(&cmd.dst_addr))
680680
return -EINVAL;
681681

682682
ctx = ucma_get_ctx(file, cmd.id);
683683
if (IS_ERR(ctx))
684684
return PTR_ERR(ctx);
685685

686-
ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms);
686+
ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr,
687+
(struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms);
687688
ucma_put_ctx(ctx);
688689
return ret;
689690
}
@@ -693,24 +694,23 @@ static ssize_t ucma_resolve_addr(struct ucma_file *file,
693694
int in_len, int out_len)
694695
{
695696
struct rdma_ucm_resolve_addr cmd;
696-
struct sockaddr *src, *dst;
697697
struct ucma_context *ctx;
698698
int ret;
699699

700700
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
701701
return -EFAULT;
702702

703-
src = (struct sockaddr *) &cmd.src_addr;
704-
dst = (struct sockaddr *) &cmd.dst_addr;
705-
if (cmd.reserved || (cmd.src_size && (cmd.src_size != rdma_addr_size(src))) ||
706-
!cmd.dst_size || (cmd.dst_size != rdma_addr_size(dst)))
703+
if (cmd.reserved ||
704+
(cmd.src_size && (cmd.src_size != rdma_addr_size_kss(&cmd.src_addr))) ||
705+
!cmd.dst_size || (cmd.dst_size != rdma_addr_size_kss(&cmd.dst_addr)))
707706
return -EINVAL;
708707

709708
ctx = ucma_get_ctx(file, cmd.id);
710709
if (IS_ERR(ctx))
711710
return PTR_ERR(ctx);
712711

713-
ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms);
712+
ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr,
713+
(struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms);
714714
ucma_put_ctx(ctx);
715715
return ret;
716716
}
@@ -1404,7 +1404,7 @@ static ssize_t ucma_join_ip_multicast(struct ucma_file *file,
14041404
join_cmd.response = cmd.response;
14051405
join_cmd.uid = cmd.uid;
14061406
join_cmd.id = cmd.id;
1407-
join_cmd.addr_size = rdma_addr_size((struct sockaddr *) &cmd.addr);
1407+
join_cmd.addr_size = rdma_addr_size_in6(&cmd.addr);
14081408
if (!join_cmd.addr_size)
14091409
return -EINVAL;
14101410

@@ -1423,7 +1423,7 @@ static ssize_t ucma_join_multicast(struct ucma_file *file,
14231423
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
14241424
return -EFAULT;
14251425

1426-
if (!rdma_addr_size((struct sockaddr *)&cmd.addr))
1426+
if (!rdma_addr_size_kss(&cmd.addr))
14271427
return -EINVAL;
14281428

14291429
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
@@ -123,6 +123,8 @@ int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev,
123123
const unsigned char *dst_dev_addr);
124124

125125
int rdma_addr_size(struct sockaddr *addr);
126+
int rdma_addr_size_in6(struct sockaddr_in6 *addr);
127+
int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr);
126128

127129
int rdma_addr_find_smac_by_sgid(union ib_gid *sgid, u8 *smac, u16 *vlan_id);
128130
int rdma_addr_find_dmac_by_grh(const union ib_gid *sgid, const union ib_gid *dgid,

0 commit comments

Comments
 (0)