summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoland Dreier <roland@purestorage.com>2018-03-28 14:27:22 -0400
committerJason Gunthorpe <jgg@mellanox.com>2018-03-28 18:13:36 -0400
commit84652aefb347297aa08e91e283adf7b18f77c2d5 (patch)
tree5a9538958172b51acede91f452829623b804a41d
parent38759d6175d338fbf9282c8ea2b51f3b7ab9bc98 (diff)
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: Roland Dreier <roland@purestorage.com> Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
-rw-r--r--drivers/infiniband/core/addr.c16
-rw-r--r--drivers/infiniband/core/ucma.c34
-rw-r--r--include/rdma/ib_addr.h2
3 files changed, 35 insertions, 17 deletions
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index c3e6811bb1bd..cb1d2ab13c66 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -207,6 +207,22 @@ int rdma_addr_size(struct sockaddr *addr)
207} 207}
208EXPORT_SYMBOL(rdma_addr_size); 208EXPORT_SYMBOL(rdma_addr_size);
209 209
210int 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}
216EXPORT_SYMBOL(rdma_addr_size_in6);
217
218int 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}
224EXPORT_SYMBOL(rdma_addr_size_kss);
225
210static struct rdma_addr_client self; 226static struct rdma_addr_client self;
211 227
212void rdma_addr_register_client(struct rdma_addr_client *client) 228void rdma_addr_register_client(struct rdma_addr_client *client)
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 21585055cf32..d933336d7e01 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -632,6 +632,9 @@ static ssize_t ucma_bind_ip(struct ucma_file *file, const char __user *inbuf,
632 if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 632 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
633 return -EFAULT; 633 return -EFAULT;
634 634
635 if (!rdma_addr_size_in6(&cmd.addr))
636 return -EINVAL;
637
635 ctx = ucma_get_ctx(file, cmd.id); 638 ctx = ucma_get_ctx(file, cmd.id);
636 if (IS_ERR(ctx)) 639 if (IS_ERR(ctx))
637 return PTR_ERR(ctx); 640 return PTR_ERR(ctx);
@@ -645,22 +648,21 @@ static ssize_t ucma_bind(struct ucma_file *file, const char __user *inbuf,
645 int in_len, int out_len) 648 int in_len, int out_len)
646{ 649{
647 struct rdma_ucm_bind cmd; 650 struct rdma_ucm_bind cmd;
648 struct sockaddr *addr;
649 struct ucma_context *ctx; 651 struct ucma_context *ctx;
650 int ret; 652 int ret;
651 653
652 if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 654 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
653 return -EFAULT; 655 return -EFAULT;
654 656
655 addr = (struct sockaddr *) &cmd.addr; 657 if (cmd.reserved || !cmd.addr_size ||
656 if (cmd.reserved || !cmd.addr_size || (cmd.addr_size != rdma_addr_size(addr))) 658 cmd.addr_size != rdma_addr_size_kss(&cmd.addr))
657 return -EINVAL; 659 return -EINVAL;
658 660
659 ctx = ucma_get_ctx(file, cmd.id); 661 ctx = ucma_get_ctx(file, cmd.id);
660 if (IS_ERR(ctx)) 662 if (IS_ERR(ctx))
661 return PTR_ERR(ctx); 663 return PTR_ERR(ctx);
662 664
663 ret = rdma_bind_addr(ctx->cm_id, addr); 665 ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr);
664 ucma_put_ctx(ctx); 666 ucma_put_ctx(ctx);
665 return ret; 667 return ret;
666} 668}
@@ -670,23 +672,22 @@ static ssize_t ucma_resolve_ip(struct ucma_file *file,
670 int in_len, int out_len) 672 int in_len, int out_len)
671{ 673{
672 struct rdma_ucm_resolve_ip cmd; 674 struct rdma_ucm_resolve_ip cmd;
673 struct sockaddr *src, *dst;
674 struct ucma_context *ctx; 675 struct ucma_context *ctx;
675 int ret; 676 int ret;
676 677
677 if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 678 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
678 return -EFAULT; 679 return -EFAULT;
679 680
680 src = (struct sockaddr *) &cmd.src_addr; 681 if (!rdma_addr_size_in6(&cmd.src_addr) ||
681 dst = (struct sockaddr *) &cmd.dst_addr; 682 !rdma_addr_size_in6(&cmd.dst_addr))
682 if (!rdma_addr_size(src) || !rdma_addr_size(dst))
683 return -EINVAL; 683 return -EINVAL;
684 684
685 ctx = ucma_get_ctx(file, cmd.id); 685 ctx = ucma_get_ctx(file, cmd.id);
686 if (IS_ERR(ctx)) 686 if (IS_ERR(ctx))
687 return PTR_ERR(ctx); 687 return PTR_ERR(ctx);
688 688
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);
690 ucma_put_ctx(ctx); 691 ucma_put_ctx(ctx);
691 return ret; 692 return ret;
692} 693}
@@ -696,24 +697,23 @@ static ssize_t ucma_resolve_addr(struct ucma_file *file,
696 int in_len, int out_len) 697 int in_len, int out_len)
697{ 698{
698 struct rdma_ucm_resolve_addr cmd; 699 struct rdma_ucm_resolve_addr cmd;
699 struct sockaddr *src, *dst;
700 struct ucma_context *ctx; 700 struct ucma_context *ctx;
701 int ret; 701 int ret;
702 702
703 if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 703 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
704 return -EFAULT; 704 return -EFAULT;
705 705
706 src = (struct sockaddr *) &cmd.src_addr; 706 if (cmd.reserved ||
707 dst = (struct sockaddr *) &cmd.dst_addr; 707 (cmd.src_size && (cmd.src_size != rdma_addr_size_kss(&cmd.src_addr))) ||
708 if (cmd.reserved || (cmd.src_size && (cmd.src_size != rdma_addr_size(src))) || 708 !cmd.dst_size || (cmd.dst_size != rdma_addr_size_kss(&cmd.dst_addr)))
709 !cmd.dst_size || (cmd.dst_size != rdma_addr_size(dst)))
710 return -EINVAL; 709 return -EINVAL;
711 710
712 ctx = ucma_get_ctx(file, cmd.id); 711 ctx = ucma_get_ctx(file, cmd.id);
713 if (IS_ERR(ctx)) 712 if (IS_ERR(ctx))
714 return PTR_ERR(ctx); 713 return PTR_ERR(ctx);
715 714
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);
717 ucma_put_ctx(ctx); 717 ucma_put_ctx(ctx);
718 return ret; 718 return ret;
719} 719}
@@ -1433,7 +1433,7 @@ static ssize_t ucma_join_ip_multicast(struct ucma_file *file,
1433 join_cmd.response = cmd.response; 1433 join_cmd.response = cmd.response;
1434 join_cmd.uid = cmd.uid; 1434 join_cmd.uid = cmd.uid;
1435 join_cmd.id = cmd.id; 1435 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);
1437 if (!join_cmd.addr_size) 1437 if (!join_cmd.addr_size)
1438 return -EINVAL; 1438 return -EINVAL;
1439 1439
@@ -1452,7 +1452,7 @@ static ssize_t ucma_join_multicast(struct ucma_file *file,
1452 if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 1452 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
1453 return -EFAULT; 1453 return -EFAULT;
1454 1454
1455 if (!rdma_addr_size((struct sockaddr *)&cmd.addr)) 1455 if (!rdma_addr_size_kss(&cmd.addr))
1456 return -EINVAL; 1456 return -EINVAL;
1457 1457
1458 return ucma_process_join(file, &cmd, out_len); 1458 return ucma_process_join(file, &cmd, out_len);
diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h
index d656809f1217..415e09960017 100644
--- a/include/rdma/ib_addr.h
+++ b/include/rdma/ib_addr.h
@@ -130,6 +130,8 @@ void rdma_copy_addr(struct rdma_dev_addr *dev_addr,
130 const unsigned char *dst_dev_addr); 130 const unsigned char *dst_dev_addr);
131 131
132int rdma_addr_size(struct sockaddr *addr); 132int rdma_addr_size(struct sockaddr *addr);
133int rdma_addr_size_in6(struct sockaddr_in6 *addr);
134int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr);
133 135
134int rdma_addr_find_l2_eth_by_grh(const union ib_gid *sgid, 136int rdma_addr_find_l2_eth_by_grh(const union ib_gid *sgid,
135 const union ib_gid *dgid, 137 const union ib_gid *dgid,