diff mbox

[v2] RDMA/ucma: Don't allow AF_IB in ucma_join_ip_multicast()

Message ID 20180328172304.7123-1-roland@kernel.org (mailing list archive)
State Superseded
Headers show

Commit Message

Roland Dreier March 28, 2018, 5:23 p.m. UTC
From: Roland Dreier <roland@purestorage.com>

If userspace passes a sockaddr with sa_family == AF_IB to the ucma join IP
multicast command, the kernel will memcpy() past the end of the cmd.addr buffer,
because sockaddr_ib is bigger than sockaddr_in6.

Fix this by returning EINVAL if the addr_size we get back is bigger than the
address buffers we're copying between.

Reported-and-tested-by: syzbot+6800425d54ed3ed8135d@syzkaller.appspotmail.com
Signed-off-by: Roland Dreier <roland@purestorage.com>
---
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git master

 drivers/infiniband/core/ucma.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

Comments

Jason Gunthorpe March 28, 2018, 5:29 p.m. UTC | #1
On Wed, Mar 28, 2018 at 10:23:04AM -0700, Roland Dreier wrote:
> From: Roland Dreier <roland@purestorage.com>
> 
> If userspace passes a sockaddr with sa_family == AF_IB to the ucma join IP
> multicast command, the kernel will memcpy() past the end of the cmd.addr buffer,
> because sockaddr_ib is bigger than sockaddr_in6.
> 
> Fix this by returning EINVAL if the addr_size we get back is bigger than the
> address buffers we're copying between.
> 
> Reported-and-tested-by: syzbot+6800425d54ed3ed8135d@syzkaller.appspotmail.com
> Signed-off-by: Roland Dreier <roland@purestorage.com>
> #syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git master
> 
>  drivers/infiniband/core/ucma.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
> index e5a1e7d81326..7684fd54318a 100644
> +++ b/drivers/infiniband/core/ucma.c
> @@ -1427,7 +1427,8 @@ static ssize_t ucma_join_ip_multicast(struct ucma_file *file,
>  	join_cmd.uid = cmd.uid;
>  	join_cmd.id = cmd.id;
>  	join_cmd.addr_size = rdma_addr_size((struct sockaddr *) &cmd.addr);

Ugh, this is a systemic mistake. Looks like every place in the uapi that uses
struct sockaddr_in6 has this bug. Eg look at ucma_bind_ip

I think I'd prefer to fix this via revising the API to

 size_t rdma_addr_size_in6(struct sockaddr_in6 *addr)
 size_t rdma_addr_size_kss(struct struct __kernel_sockaddr_storage *addr)

And getting rid of all the dangerous (struct sockaddr *) casting at
all the various call sites.

The function will return 0 if the given storage is too small for the
requested AF, or the AF is not supported.

Jason
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
syzbot March 28, 2018, 5:50 p.m. UTC | #2
Hello,

syzbot has tested the proposed patch and the reproducer did not trigger  
crash:

Reported-and-tested-by:  
syzbot+6800425d54ed3ed8135d@syzkaller.appspotmail.com

Note: the tag will also help syzbot to understand when the bug is fixed.

Tested on  
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git/master  
commit
3eb2ce825ea1ad89d20f7a3b5780df850e4be274 (Sun Mar 25 22:44:30 2018 +0000)
Linux 4.16-rc7

compiler: gcc (GCC) 7.1.1 20170620
Patch: https://syzkaller.appspot.com/x/patch.diff?id=6405865665986560
Kernel config:  
https://syzkaller.appspot.com/x/.config?id=-2340295454854568752


---
There is no WARRANTY for the result, to the extent permitted by applicable  
law.
Except when otherwise stated in writing syzbot provides the result "AS IS"
without warranty of any kind, either expressed or implied, but not limited  
to,
the implied warranties of merchantability and fittness for a particular  
purpose.
The entire risk as to the quality of the result is with you. Should the  
result
prove defective, you assume the cost of all necessary servicing, repair or
correction.
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Roland Dreier March 28, 2018, 5:57 p.m. UTC | #3
> Ugh, this is a systemic mistake. Looks like every place in the uapi that uses
> struct sockaddr_in6 has this bug. Eg look at ucma_bind_ip
>
> I think I'd prefer to fix this via revising the API to
>
>  size_t rdma_addr_size_in6(struct sockaddr_in6 *addr)
>  size_t rdma_addr_size_kss(struct struct __kernel_sockaddr_storage *addr)
>
> And getting rid of all the dangerous (struct sockaddr *) casting at
> all the various call sites.
>
> The function will return 0 if the given storage is too small for the
> requested AF, or the AF is not supported.

Good idea.  I'll code that up.

 - R.
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index e5a1e7d81326..7684fd54318a 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -1427,7 +1427,8 @@  static ssize_t ucma_join_ip_multicast(struct ucma_file *file,
 	join_cmd.uid = cmd.uid;
 	join_cmd.id = cmd.id;
 	join_cmd.addr_size = rdma_addr_size((struct sockaddr *) &cmd.addr);
-	if (!join_cmd.addr_size)
+	if (!join_cmd.addr_size ||
+	    join_cmd.addr_size > min(sizeof(cmd.addr), sizeof(join_cmd.addr)))
 		return -EINVAL;
 
 	join_cmd.join_flags = RDMA_MC_JOIN_FLAG_FULLMEMBER;