diff mbox series

[for-rc] IB/qib: Fix issues noted by fuzzing on the iovecs

Message ID 20211004115625.118981.81200.stgit@awfm-01.cornelisnetworks.com (mailing list archive)
State Changes Requested
Headers show
Series [for-rc] IB/qib: Fix issues noted by fuzzing on the iovecs | expand

Commit Message

Dennis Dalessandro Oct. 4, 2021, 11:56 a.m. UTC
From: Mike Marciniszyn <mike.marciniszyn@cornelisnetworks.com>

Add protection for bytes_togo and n to avoid going beyond variables
in PSM pkt structure.

Reported-by: Ilja Van Sprundel <ivansprundel@ioactive.com>
Reviewed-by: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
Signed-off-by: Mike Marciniszyn <mike.marciniszyn@cornelisnetworks.com>
Signed-off-by: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
---
 drivers/infiniband/hw/qib/qib_user_sdma.c |   20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

Comments

Dennis Dalessandro Oct. 4, 2021, 5:17 p.m. UTC | #1
On 10/4/21 7:56 AM, Dennis Dalessandro wrote:
> From: Mike Marciniszyn <mike.marciniszyn@cornelisnetworks.com>
> 
> Add protection for bytes_togo and n to avoid going beyond variables
> in PSM pkt structure.
> 
> Reported-by: Ilja Van Sprundel <ivansprundel@ioactive.com>
> Reviewed-by: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
> Signed-off-by: Mike Marciniszyn <mike.marciniszyn@cornelisnetworks.com>
> Signed-off-by: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>

Might want to add:

Fixes: f931551bafe1 ("IB/qib: Add new qib driver for QLogic PCIe InfiniBand
adapters")

-Denny
Jason Gunthorpe Oct. 5, 2021, 6:51 p.m. UTC | #2
On Mon, Oct 04, 2021 at 07:56:25AM -0400, Dennis Dalessandro wrote:
> From: Mike Marciniszyn <mike.marciniszyn@cornelisnetworks.com>
> 
> Add protection for bytes_togo and n to avoid going beyond variables
> in PSM pkt structure.
> 
> Reported-by: Ilja Van Sprundel <ivansprundel@ioactive.com>
> Reviewed-by: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
> Signed-off-by: Mike Marciniszyn <mike.marciniszyn@cornelisnetworks.com>
> Signed-off-by: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
>  drivers/infiniband/hw/qib/qib_user_sdma.c |   20 ++++++++++++--------
>  1 file changed, 12 insertions(+), 8 deletions(-)

This commit message isn't good enough for a rc patch

    IB/qib: Protect from buffer overflow in struct qib_user_sdma_pkt::addr

    Overflowing either n or bytes_togo can allow userspace to trigger a buffer
    overflow of kernel memory. Check for overflows in all the places
    doing math on user controlled buffers

Don't forget the Fixes line.

  
> @@ -878,7 +878,7 @@ static int qib_user_sdma_queue_pkts(const struct qib_devdata *dd,
>  			const unsigned long faddr =
>  				(unsigned long) iov[idx].iov_base;
>  
> -			if (slen & 3 || faddr & 3 || !slen) {
> +			if (slen & 3 || faddr & 3 || !slen || bytes_togo >= (U32_MAX-slen)) {
>  				ret = -EINVAL;
>  				goto free_pbc;
>  			}

This is not the expected way to write overflow checks these days, like this:

-                       bytes_togo += slen;
+                       if (check_add_overflow(bytes_togo, slen, &bytes_togo)) {
+                               ret = -EINVAL;
+                               goto free_pbc;
+                       }


> @@ -904,10 +904,14 @@ static int qib_user_sdma_queue_pkts(const struct qib_devdata *dd,
>  		}
>  
>  		if (frag_size) {
> -			int tidsmsize, n;
> -			size_t pktsize;
> +			size_t tidsmsize, n, pktsize;
>  
>  			n = npages*((2*PAGE_SIZE/frag_size)+1);
> +			if (n >= (U16_MAX - ARRAY_SIZE(pkt->addr))) {
> +				ret = -EINVAL;
> +				goto free_pbc;
> +			}
> +
>  			pktsize = struct_size(pkt, addr, n);

And I think this is supposed to be more like this:

-                       if (n >= (U16_MAX - ARRAY_SIZE(pkt->addr))) {
+                       if (check_add_overflow(n, ARRAY_SIZE(pkt->addr),
+                                              &addrlimit) ||
+                           addrlimit > type_max(typeof(pkt->addrlimit))) {
                                ret = -EINVAL;
-                               goto free_pbc;
+                               goto free pbc;
                        }

And this is probably missing too:

-                       pkt = kmalloc(pktsize+tidsmsize, GFP_KERNEL);
+                       pktsize = struct_size(pkt, addr, n);
+                       if (check_add_overflow(pktsize, tidsmsize,
+                                              &allocsize)) {
+                               ret = -EINVAL;
+                               goto free_pbc;
+                       }
+                       pkt = kmalloc(allocsize, GFP_KERNEL);

The struct_size() of this:

struct qib_user_sdma_pkt {
	struct {
	} addr[4];   /* max pages, any more and we coalesce */
};

Is also super weird, that addr[4] should typically be [0]

Jason
diff mbox series

Patch

diff --git a/drivers/infiniband/hw/qib/qib_user_sdma.c b/drivers/infiniband/hw/qib/qib_user_sdma.c
index a67599b..144c3de 100644
--- a/drivers/infiniband/hw/qib/qib_user_sdma.c
+++ b/drivers/infiniband/hw/qib/qib_user_sdma.c
@@ -602,7 +602,7 @@  static int qib_user_sdma_coalesce(const struct qib_devdata *dd,
 /*
  * How many pages in this iovec element?
  */
-static int qib_user_sdma_num_pages(const struct iovec *iov)
+static size_t qib_user_sdma_num_pages(const struct iovec *iov)
 {
 	const unsigned long addr  = (unsigned long) iov->iov_base;
 	const unsigned long  len  = iov->iov_len;
@@ -658,7 +658,7 @@  static void qib_user_sdma_free_pkt_frag(struct device *dev,
 static int qib_user_sdma_pin_pages(const struct qib_devdata *dd,
 				   struct qib_user_sdma_queue *pq,
 				   struct qib_user_sdma_pkt *pkt,
-				   unsigned long addr, int tlen, int npages)
+				   unsigned long addr, int tlen, size_t npages)
 {
 	struct page *pages[8];
 	int i, j;
@@ -722,7 +722,7 @@  static int qib_user_sdma_pin_pkt(const struct qib_devdata *dd,
 	unsigned long idx;
 
 	for (idx = 0; idx < niov; idx++) {
-		const int npages = qib_user_sdma_num_pages(iov + idx);
+		const size_t npages = qib_user_sdma_num_pages(iov + idx);
 		const unsigned long addr = (unsigned long) iov[idx].iov_base;
 
 		ret = qib_user_sdma_pin_pages(dd, pq, pkt, addr,
@@ -824,8 +824,8 @@  static int qib_user_sdma_queue_pkts(const struct qib_devdata *dd,
 		unsigned pktnw;
 		unsigned pktnwc;
 		int nfrags = 0;
-		int npages = 0;
-		int bytes_togo = 0;
+		size_t npages = 0;
+		size_t bytes_togo = 0;
 		int tiddma = 0;
 		int cfur;
 
@@ -878,7 +878,7 @@  static int qib_user_sdma_queue_pkts(const struct qib_devdata *dd,
 			const unsigned long faddr =
 				(unsigned long) iov[idx].iov_base;
 
-			if (slen & 3 || faddr & 3 || !slen) {
+			if (slen & 3 || faddr & 3 || !slen || bytes_togo >= (U32_MAX-slen)) {
 				ret = -EINVAL;
 				goto free_pbc;
 			}
@@ -904,10 +904,14 @@  static int qib_user_sdma_queue_pkts(const struct qib_devdata *dd,
 		}
 
 		if (frag_size) {
-			int tidsmsize, n;
-			size_t pktsize;
+			size_t tidsmsize, n, pktsize;
 
 			n = npages*((2*PAGE_SIZE/frag_size)+1);
+			if (n >= (U16_MAX - ARRAY_SIZE(pkt->addr))) {
+				ret = -EINVAL;
+				goto free_pbc;
+			}
+
 			pktsize = struct_size(pkt, addr, n);
 
 			/*