linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [V9fs-developer] [PATCH] p9_parse_header() validate PDU length
@ 2018-07-12 11:02 Tomas Bortoli
  2018-07-12 11:43 ` Dominique Martinet
  2018-07-18  5:13 ` Dominique Martinet
  0 siblings, 2 replies; 6+ messages in thread
From: Tomas Bortoli @ 2018-07-12 11:02 UTC (permalink / raw)
  To: ericvh, rminnich, lucho
  Cc: asmadeus, viro, davem, v9fs-developer, netdev, linux-kernel,
	syzkaller, Tomas Bortoli

This patch adds checks to the p9_parse_header() function to
verify that the length found within the header coincides with the actual
length of the PDU. Furthermore, it checks that the length stays within the
acceptable range. To do this the patch brings the actual length of the PDU
from the different transport layers (rdma and virtio). For TCP (trans_fd.c)
the length is not know before, so we get it from the header but we check it
anyway that it's within the valid range.

Signed-off-by: Tomas Bortoli <tomasbortoli@gmail.com>
Reported-by: syzbot+65c6b72f284a39d416b4@syzkaller.appspotmail.com
---
 net/9p/client.c       | 26 ++++++++++++++++----------
 net/9p/trans_fd.c     | 17 ++++++-----------
 net/9p/trans_rdma.c   |  2 +-
 net/9p/trans_virtio.c |  6 ++++--
 4 files changed, 27 insertions(+), 24 deletions(-)

diff --git a/net/9p/client.c b/net/9p/client.c
index 18c5271910dc..119de44f49e9 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -477,20 +477,11 @@ p9_parse_header(struct p9_fcall *pdu, int32_t *size, int8_t *type, int16_t *tag,
 	int err;
 
 	pdu->offset = 0;
-	if (pdu->size == 0)
-		pdu->size = 7;
 
 	err = p9pdu_readf(pdu, 0, "dbw", &r_size, &r_type, &r_tag);
 	if (err)
 		goto rewind_and_exit;
 
-	pdu->size = r_size;
-	pdu->id = r_type;
-	pdu->tag = r_tag;
-
-	p9_debug(P9_DEBUG_9P, "<<< size=%d type: %d tag: %d\n",
-		 pdu->size, pdu->id, pdu->tag);
-
 	if (type)
 		*type = r_type;
 	if (tag)
@@ -498,6 +489,21 @@ p9_parse_header(struct p9_fcall *pdu, int32_t *size, int8_t *type, int16_t *tag,
 	if (size)
 		*size = r_size;
 
+	if (pdu->size != r_size) {
+		err = -EINVAL;
+		goto rewind_and_exit;
+	}
+	if (pdu->size >= pdu->capacity || pdu->size < 7) {
+		p9_debug(P9_DEBUG_ERROR,
+			 "requested packet size too big or too small: %d\n",
+			 pdu->size);
+		return -EIO;
+	}
+	pdu->id = r_type;
+	pdu->tag = r_tag;
+
+	p9_debug(P9_DEBUG_9P, "<<< size=%d type: %d tag: %d\n",
+		 pdu->size, pdu->id, pdu->tag);
 
 rewind_and_exit:
 	if (rewind)
@@ -1575,7 +1581,7 @@ p9_client_read(struct p9_fid *fid, u64 offset, struct iov_iter *to, int *err)
 		int count = iov_iter_count(to);
 		int rsize, non_zc = 0;
 		char *dataptr;
-			
+
 		rsize = fid->iounit;
 		if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
 			rsize = clnt->msize - P9_IOHDRSZ;
diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c
index 588bf88c3305..bf459ee0feab 100644
--- a/net/9p/trans_fd.c
+++ b/net/9p/trans_fd.c
@@ -323,22 +323,16 @@ static void p9_read_work(struct work_struct *work)
 	/* header read in */
 	if ((!m->req) && (m->rc.offset == m->rc.capacity)) {
 		p9_debug(P9_DEBUG_TRANS, "got new header\n");
-
-		err = p9_parse_header(&m->rc, NULL, NULL, NULL, 0);
+		/* Header size */
+		m->rc.size = 7;
+		m->rc.capacity = m->client->msize;
+		err = p9_parse_header(&m->rc, &m->rc.size, NULL, NULL, 0);
 		if (err) {
 			p9_debug(P9_DEBUG_ERROR,
 				 "error parsing header: %d\n", err);
 			goto error;
 		}
 
-		if (m->rc.size >= m->client->msize) {
-			p9_debug(P9_DEBUG_ERROR,
-				 "requested packet size too big: %d\n",
-				 m->rc.size);
-			err = -EIO;
-			goto error;
-		}
-
 		p9_debug(P9_DEBUG_TRANS,
 			 "mux %p pkt: size: %d bytes tag: %d\n",
 			 m, m->rc.size, m->rc.tag);
@@ -360,7 +354,7 @@ static void p9_read_work(struct work_struct *work)
 			goto error;
 		}
 		m->rc.sdata = (char *)m->req->rc + sizeof(struct p9_fcall);
-		memcpy(m->rc.sdata, m->tmp_buf, m->rc.capacity);
+		memcpy(m->rc.sdata, m->tmp_buf, 7);
 		m->rc.capacity = m->rc.size;
 	}
 
@@ -369,6 +363,7 @@ static void p9_read_work(struct work_struct *work)
 	 */
 	if ((m->req) && (m->rc.offset == m->rc.capacity)) {
 		p9_debug(P9_DEBUG_TRANS, "got new packet\n");
+		m->req->rc->size = m->rc.offset;
 		spin_lock(&m->client->lock);
 		if (m->req->status != REQ_STATUS_ERROR)
 			status = REQ_STATUS_RCVD;
diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c
index 3d414acb7015..002badbcc9c0 100644
--- a/net/9p/trans_rdma.c
+++ b/net/9p/trans_rdma.c
@@ -319,7 +319,7 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc)
 
 	if (wc->status != IB_WC_SUCCESS)
 		goto err_out;
-
+	c->rc->size = wc->byte_len;
 	err = p9_parse_header(c->rc, NULL, NULL, &tag, 1);
 	if (err)
 		goto err_out;
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c
index 05006cbb3361..6d515f7ebfaf 100644
--- a/net/9p/trans_virtio.c
+++ b/net/9p/trans_virtio.c
@@ -159,8 +159,10 @@ static void req_done(struct virtqueue *vq)
 		spin_unlock_irqrestore(&chan->lock, flags);
 		/* Wakeup if anyone waiting for VirtIO ring space. */
 		wake_up(chan->vc_wq);
-		if (len)
+		if (len) {
+			req->rc->size = len;
 			p9_client_cb(chan->client, req, REQ_STATUS_RCVD);
+		}
 	}
 }
 
@@ -446,7 +448,7 @@ p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req,
 		out += pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM,
 				      out_pages, out_nr_pages, offs, outlen);
 	}
-		
+
 	/*
 	 * Take care of in data
 	 * For example TREAD have 11.
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [V9fs-developer] [PATCH] p9_parse_header() validate PDU length
  2018-07-12 11:02 [V9fs-developer] [PATCH] p9_parse_header() validate PDU length Tomas Bortoli
@ 2018-07-12 11:43 ` Dominique Martinet
  2018-07-12 16:19   ` Tomas Bortoli
  2018-07-18  5:13 ` Dominique Martinet
  1 sibling, 1 reply; 6+ messages in thread
From: Dominique Martinet @ 2018-07-12 11:43 UTC (permalink / raw)
  To: Tomas Bortoli
  Cc: ericvh, rminnich, lucho, viro, davem, v9fs-developer, netdev,
	linux-kernel, syzkaller

Tomas Bortoli wrote on Thu, Jul 12, 2018:
> This patch adds checks to the p9_parse_header() function to
> verify that the length found within the header coincides with the actual
> length of the PDU. Furthermore, it checks that the length stays within the
> acceptable range. To do this the patch brings the actual length of the PDU
> from the different transport layers (rdma and virtio). For TCP (trans_fd.c)
> the length is not know before, so we get it from the header but we check it
> anyway that it's within the valid range.

Just a note on transports here, I totally had forgotten about trans_xen
when we discussed this earlier as it is fairly new, but it looks like it
sets the length in the fcall properly so it should work without any
change.

I however cannot test trans=xen, so if someone could either point me to
how to set that up (I couldn't find any decent documentation) or do some
very basic tests that would be great.

> Signed-off-by: Tomas Bortoli <tomasbortoli@gmail.com>
> Reported-by: syzbot+65c6b72f284a39d416b4@syzkaller.appspotmail.com

Looks good to me, as the rdma/virtio part come from my suggestion:
Signed-off-by: Dominique Martinet <dominique.martinet@cea.fr>

> diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c
> index 3d414acb7015..002badbcc9c0 100644
> --- a/net/9p/trans_rdma.c
> +++ b/net/9p/trans_rdma.c
> @@ -319,7 +319,7 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc)
>  
>  	if (wc->status != IB_WC_SUCCESS)
>  		goto err_out;
> -
> +	c->rc->size = wc->byte_len;

(nitpick, I'd keep the empty line here. If you don't mind I'll add it
back in my tree; this doesn't warrant a v2)

-- 
Dominique Martinet

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [V9fs-developer] [PATCH] p9_parse_header() validate PDU length
  2018-07-12 11:43 ` Dominique Martinet
@ 2018-07-12 16:19   ` Tomas Bortoli
  2018-07-18  5:08     ` Dominique Martinet
  0 siblings, 1 reply; 6+ messages in thread
From: Tomas Bortoli @ 2018-07-12 16:19 UTC (permalink / raw)
  To: Dominique Martinet
  Cc: ericvh, rminnich, lucho, viro, davem, v9fs-developer, netdev,
	linux-kernel, syzkaller, Andrew Morton

+ Cc: Andrew Morton <akpm@linux-foundation.org>

On 07/12/2018 01:43 PM, Dominique Martinet wrote:
> Tomas Bortoli wrote on Thu, Jul 12, 2018:
>> This patch adds checks to the p9_parse_header() function to
>> verify that the length found within the header coincides with the actual
>> length of the PDU. Furthermore, it checks that the length stays within the
>> acceptable range. To do this the patch brings the actual length of the PDU
>> from the different transport layers (rdma and virtio). For TCP (trans_fd.c)
>> the length is not know before, so we get it from the header but we check it
>> anyway that it's within the valid range.

Still for TCP it you could read "garbage" pre-allocated memory but I
don't know how much it is a risk, it might be a good idea to zero it
post allocation (I mean pdu->sdata). Allocated at:

https://github.com/torvalds/linux/blob/master/net/9p/client.c#L236

> Just a note on transports here, I totally had forgotten about trans_xen
> when we discussed this earlier as it is fairly new, but it looks like it
> sets the length in the fcall properly so it should work without any
> change.
>
> I however cannot test trans=xen, so if someone could either point me to
> how to set that up (I couldn't find any decent documentation) or do some
> very basic tests that would be great.

>> Signed-off-by: Tomas Bortoli <tomasbortoli@gmail.com>
>> Reported-by: syzbot+65c6b72f284a39d416b4@syzkaller.appspotmail.com
> Looks good to me, as the rdma/virtio part come from my suggestion:
> Signed-off-by: Dominique Martinet <dominique.martinet@cea.fr>

True
>
>> diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c
>> index 3d414acb7015..002badbcc9c0 100644
>> --- a/net/9p/trans_rdma.c
>> +++ b/net/9p/trans_rdma.c
>> @@ -319,7 +319,7 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc)
>>  
>>  	if (wc->status != IB_WC_SUCCESS)
>>  		goto err_out;
>> -
>> +	c->rc->size = wc->byte_len;
> (nitpick, I'd keep the empty line here. If you don't mind I'll add it
> back in my tree; this doesn't warrant a v2)
>

Sure,

Tomas


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [V9fs-developer] [PATCH] p9_parse_header() validate PDU length
  2018-07-12 16:19   ` Tomas Bortoli
@ 2018-07-18  5:08     ` Dominique Martinet
  0 siblings, 0 replies; 6+ messages in thread
From: Dominique Martinet @ 2018-07-18  5:08 UTC (permalink / raw)
  To: Tomas Bortoli
  Cc: ericvh, rminnich, lucho, viro, davem, v9fs-developer, netdev,
	linux-kernel, syzkaller, Andrew Morton

Tomas Bortoli wrote on Thu, Jul 12, 2018:
> + Cc: Andrew Morton <akpm@linux-foundation.org>
> 
> On 07/12/2018 01:43 PM, Dominique Martinet wrote:
> > Tomas Bortoli wrote on Thu, Jul 12, 2018:
> >> This patch adds checks to the p9_parse_header() function to
> >> verify that the length found within the header coincides with the actual
> >> length of the PDU. Furthermore, it checks that the length stays within the
> >> acceptable range. To do this the patch brings the actual length of the PDU
> >> from the different transport layers (rdma and virtio). For TCP (trans_fd.c)
> >> the length is not know before, so we get it from the header but we check it
> >> anyway that it's within the valid range.
> 
> Still for TCP it you could read "garbage" pre-allocated memory but I
> don't know how much it is a risk, it might be a good idea to zero it
> post allocation (I mean pdu->sdata). Allocated at:
> 
> https://github.com/torvalds/linux/blob/master/net/9p/client.c#L236
> 
> > Just a note on transports here, I totally had forgotten about trans_xen
> > when we discussed this earlier as it is fairly new, but it looks like it
> > sets the length in the fcall properly so it should work without any
> > change.
> >
> > I however cannot test trans=xen, so if someone could either point me to
> > how to set that up (I couldn't find any decent documentation) or do some
> > very basic tests that would be great.
> 
> >> Signed-off-by: Tomas Bortoli <tomasbortoli@gmail.com>
> >> Reported-by: syzbot+65c6b72f284a39d416b4@syzkaller.appspotmail.com
> > Looks good to me, as the rdma/virtio part come from my suggestion:
> > Signed-off-by: Dominique Martinet <dominique.martinet@cea.fr>
> 
> True
> >
> >> diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c
> >> index 3d414acb7015..002badbcc9c0 100644
> >> --- a/net/9p/trans_rdma.c
> >> +++ b/net/9p/trans_rdma.c
> >> @@ -319,7 +319,7 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc)
> >>  
> >>  	if (wc->status != IB_WC_SUCCESS)
> >>  		goto err_out;
> >> -
> >> +	c->rc->size = wc->byte_len;
> > (nitpick, I'd keep the empty line here. If you don't mind I'll add it
> > back in my tree; this doesn't warrant a v2)
> >
> 
> Sure,
> 
> Tomas
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [V9fs-developer] [PATCH] p9_parse_header() validate PDU length
  2018-07-12 11:02 [V9fs-developer] [PATCH] p9_parse_header() validate PDU length Tomas Bortoli
  2018-07-12 11:43 ` Dominique Martinet
@ 2018-07-18  5:13 ` Dominique Martinet
  2018-07-18  8:39   ` Tomas Bortoli
  1 sibling, 1 reply; 6+ messages in thread
From: Dominique Martinet @ 2018-07-18  5:13 UTC (permalink / raw)
  To: Tomas Bortoli
  Cc: ericvh, rminnich, lucho, viro, davem, v9fs-developer, netdev,
	linux-kernel, syzkaller

Tomas Bortoli wrote on Thu, Jul 12, 2018:
> This patch adds checks to the p9_parse_header() function to
> verify that the length found within the header coincides with the actual
> length of the PDU. Furthermore, it checks that the length stays within the
> acceptable range. To do this the patch brings the actual length of the PDU
> from the different transport layers (rdma and virtio). For TCP (trans_fd.c)
> the length is not know before, so we get it from the header but we check it
> anyway that it's within the valid range.
> 
> Signed-off-by: Tomas Bortoli <tomasbortoli@gmail.com>
> Reported-by: syzbot+65c6b72f284a39d416b4@syzkaller.appspotmail.com
> ---
> [..]
> @@ -498,6 +489,21 @@ p9_parse_header(struct p9_fcall *pdu, int32_t *size, int8_t *type, int16_t *tag,
>  	if (size)
>  		*size = r_size;
>  
> +	if (pdu->size != r_size) {
> +		err = -EINVAL;
> +		goto rewind_and_exit;
> +	}
> +	if (pdu->size >= pdu->capacity || pdu->size < 7) {
> +		p9_debug(P9_DEBUG_ERROR,
> +			 "requested packet size too big or too small: %d\n",
> +			 pdu->size);
> +		return -EIO;
> +	}

Actually, I've been bad advice - this breaks on virtio with zc packets -
a read or ls in a big directory fails with this in dmesg
[ 1006.853775] 9pnet: -- p9_parse_header (17123): requested packet size too big or too small: 4306
[ 1006.853780] 9pnet: -- p9_check_zc_errors (17123): couldn't parse header -5

I haven't given this any thought yet, but dropping the patch for now

-- 
Dominique

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [V9fs-developer] [PATCH] p9_parse_header() validate PDU length
  2018-07-18  5:13 ` Dominique Martinet
@ 2018-07-18  8:39   ` Tomas Bortoli
  0 siblings, 0 replies; 6+ messages in thread
From: Tomas Bortoli @ 2018-07-18  8:39 UTC (permalink / raw)
  To: Dominique Martinet
  Cc: ericvh, rminnich, lucho, viro, davem, v9fs-developer, netdev,
	linux-kernel, syzkaller

On 07/18/2018 07:13 AM, Dominique Martinet wrote:
> Tomas Bortoli wrote on Thu, Jul 12, 2018:
>> This patch adds checks to the p9_parse_header() function to
>> verify that the length found within the header coincides with the actual
>> length of the PDU. Furthermore, it checks that the length stays within the
>> acceptable range. To do this the patch brings the actual length of the PDU
>> from the different transport layers (rdma and virtio). For TCP (trans_fd.c)
>> the length is not know before, so we get it from the header but we check it
>> anyway that it's within the valid range.
>>
>> Signed-off-by: Tomas Bortoli <tomasbortoli@gmail.com>
>> Reported-by: syzbot+65c6b72f284a39d416b4@syzkaller.appspotmail.com
>> ---
>> [..]
>> @@ -498,6 +489,21 @@ p9_parse_header(struct p9_fcall *pdu, int32_t *size, int8_t *type, int16_t *tag,
>>  	if (size)
>>  		*size = r_size;
>>  
>> +	if (pdu->size != r_size) {
>> +		err = -EINVAL;
>> +		goto rewind_and_exit;
>> +	}
>> +	if (pdu->size >= pdu->capacity || pdu->size < 7) {
>> +		p9_debug(P9_DEBUG_ERROR,
>> +			 "requested packet size too big or too small: %d\n",
>> +			 pdu->size);
>> +		return -EIO;
>> +	}
> Actually, I've been bad advice - this breaks on virtio with zc packets -
> a read or ls in a big directory fails with this in dmesg
> [ 1006.853775] 9pnet: -- p9_parse_header (17123): requested packet size too big or too small: 4306
> [ 1006.853780] 9pnet: -- p9_check_zc_errors (17123): couldn't parse header -5
>
> I haven't given this any thought yet, but dropping the patch for now
>
It seems that pdu->capacity is set to less than 4306 at that point, we
can just increase it.


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-07-18  8:40 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-12 11:02 [V9fs-developer] [PATCH] p9_parse_header() validate PDU length Tomas Bortoli
2018-07-12 11:43 ` Dominique Martinet
2018-07-12 16:19   ` Tomas Bortoli
2018-07-18  5:08     ` Dominique Martinet
2018-07-18  5:13 ` Dominique Martinet
2018-07-18  8:39   ` Tomas Bortoli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).