diff mbox

[1/2] virtio-net: Verify page list size before fitting into skb

Message ID 1317058869-19276-1-git-send-email-levinsasha928@gmail.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Sasha Levin Sept. 26, 2011, 5:41 p.m. UTC
This patch verifies that the length of a buffer stored in a linked list
of pages is small enough to fit into a skb.

If the size is larger than a max size of a skb, it means that we shouldn't
go ahead building skbs anyway since we won't be able to send the buffer as
the user requested.

Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: virtualization@lists.linux-foundation.org
Cc: netdev@vger.kernel.org
Cc: kvm@vger.kernel.org
Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
---
 drivers/net/virtio_net.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

Comments

Michael S. Tsirkin Sept. 26, 2011, 6:44 p.m. UTC | #1
On Mon, Sep 26, 2011 at 08:41:08PM +0300, Sasha Levin wrote:
> This patch verifies that the length of a buffer stored in a linked list
> of pages is small enough to fit into a skb.
> 
> If the size is larger than a max size of a skb, it means that we shouldn't
> go ahead building skbs anyway since we won't be able to send the buffer as
> the user requested.
> 
> Cc: Rusty Russell <rusty@rustcorp.com.au>
> Cc: "Michael S. Tsirkin" <mst@redhat.com>
> Cc: virtualization@lists.linux-foundation.org
> Cc: netdev@vger.kernel.org
> Cc: kvm@vger.kernel.org
> Signed-off-by: Sasha Levin <levinsasha928@gmail.com>

Interesting.  This is a theoretical issue, correct?
Not a crash you actually see.

This crash would mean device is giving us packets
that are way too large. Avoiding crashes even in the face of
a misbehaved device is a good idea, but should
we print a diagnostic to a system log?
Maybe rate-limited or print once to avoid filling
up the disk. Other places in driver print with pr_debug
I'm not sure that's right but better than nothing.

> ---
>  drivers/net/virtio_net.c |    3 +++
>  1 files changed, 3 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 0c7321c..64e0717 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -165,6 +165,9 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi,
>  	unsigned int copy, hdr_len, offset;
>  	char *p;
>  
> +	if (len > MAX_SKB_FRAGS * PAGE_SIZE)

unlikely()?

Also, this seems too aggressive: at this point len includes the header
and the linear part. The right place for this
test is probably where we fill in the frags, just before
while (len)

The whole can only happen when mergeable buffers
are disabled, right?


> +		return NULL;
> +
>  	p = page_address(page);
>  
>  	/* copy small packet so we can reuse these pages for small data */
> -- 
> 1.7.6.1
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sasha Levin Sept. 26, 2011, 7:37 p.m. UTC | #2
On Mon, 2011-09-26 at 21:44 +0300, Michael S. Tsirkin wrote:
> On Mon, Sep 26, 2011 at 08:41:08PM +0300, Sasha Levin wrote:
> > This patch verifies that the length of a buffer stored in a linked list
> > of pages is small enough to fit into a skb.
> > 
> > If the size is larger than a max size of a skb, it means that we shouldn't
> > go ahead building skbs anyway since we won't be able to send the buffer as
> > the user requested.
> > 
> > Cc: Rusty Russell <rusty@rustcorp.com.au>
> > Cc: "Michael S. Tsirkin" <mst@redhat.com>
> > Cc: virtualization@lists.linux-foundation.org
> > Cc: netdev@vger.kernel.org
> > Cc: kvm@vger.kernel.org
> > Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
> 
> Interesting.  This is a theoretical issue, correct?
> Not a crash you actually see.

Actually it was an actual crash caused when our virtio-net driver in kvm
tools did funny things and passed '(u32)-1' length as a buffer length to
the guest kernel.

> This crash would mean device is giving us packets
> that are way too large. Avoiding crashes even in the face of
> a misbehaved device is a good idea, but should
> we print a diagnostic to a system log?
> Maybe rate-limited or print once to avoid filling
> up the disk. Other places in driver print with pr_debug
> I'm not sure that's right but better than nothing.

Yup, I'll add some debug info.

> > ---
> >  drivers/net/virtio_net.c |    3 +++
> >  1 files changed, 3 insertions(+), 0 deletions(-)
> > 
> > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> > index 0c7321c..64e0717 100644
> > --- a/drivers/net/virtio_net.c
> > +++ b/drivers/net/virtio_net.c
> > @@ -165,6 +165,9 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi,
> >  	unsigned int copy, hdr_len, offset;
> >  	char *p;
> >  
> > +	if (len > MAX_SKB_FRAGS * PAGE_SIZE)
> 
> unlikely()?
> 
> Also, this seems too aggressive: at this point len includes the header
> and the linear part. The right place for this
> test is probably where we fill in the frags, just before
> while (len)
> 
> The whole can only happen when mergeable buffers
> are disabled, right?

From what I understand it can happen whenever you're going to build a
skb longer than PAGE_SIZE.
Pekka Enberg Sept. 26, 2011, 7:45 p.m. UTC | #3
On Mon, Sep 26, 2011 at 10:37 PM, Sasha Levin <levinsasha928@gmail.com> wrote:
>> Interesting.  This is a theoretical issue, correct?
>> Not a crash you actually see.
>
> Actually it was an actual crash caused when our virtio-net driver in kvm
> tools did funny things and passed '(u32)-1' length as a buffer length to
> the guest kernel.

I'm not sure what Michael means with "theoretical issue" here. Can the guest
driver assume that the hypervisor doesn't attempt to do nasty things?

                          Pekka
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michael S. Tsirkin Sept. 26, 2011, 7:55 p.m. UTC | #4
On Mon, Sep 26, 2011 at 10:37:22PM +0300, Sasha Levin wrote:
> On Mon, 2011-09-26 at 21:44 +0300, Michael S. Tsirkin wrote:
> > On Mon, Sep 26, 2011 at 08:41:08PM +0300, Sasha Levin wrote:
> > > This patch verifies that the length of a buffer stored in a linked list
> > > of pages is small enough to fit into a skb.
> > > 
> > > If the size is larger than a max size of a skb, it means that we shouldn't
> > > go ahead building skbs anyway since we won't be able to send the buffer as
> > > the user requested.
> > > 
> > > Cc: Rusty Russell <rusty@rustcorp.com.au>
> > > Cc: "Michael S. Tsirkin" <mst@redhat.com>
> > > Cc: virtualization@lists.linux-foundation.org
> > > Cc: netdev@vger.kernel.org
> > > Cc: kvm@vger.kernel.org
> > > Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
> > 
> > Interesting.  This is a theoretical issue, correct?
> > Not a crash you actually see.
> 
> Actually it was an actual crash caused when our virtio-net driver in kvm
> tools did funny things and passed '(u32)-1' length as a buffer length to
> the guest kernel.
> 
> > This crash would mean device is giving us packets
> > that are way too large. Avoiding crashes even in the face of
> > a misbehaved device is a good idea, but should
> > we print a diagnostic to a system log?
> > Maybe rate-limited or print once to avoid filling
> > up the disk. Other places in driver print with pr_debug
> > I'm not sure that's right but better than nothing.
> 
> Yup, I'll add some debug info.
> 
> > > ---
> > >  drivers/net/virtio_net.c |    3 +++
> > >  1 files changed, 3 insertions(+), 0 deletions(-)
> > > 
> > > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> > > index 0c7321c..64e0717 100644
> > > --- a/drivers/net/virtio_net.c
> > > +++ b/drivers/net/virtio_net.c
> > > @@ -165,6 +165,9 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi,
> > >  	unsigned int copy, hdr_len, offset;
> > >  	char *p;
> > >  
> > > +	if (len > MAX_SKB_FRAGS * PAGE_SIZE)
> > 
> > unlikely()?
> > 
> > Also, this seems too aggressive: at this point len includes the header
> > and the linear part. The right place for this
> > test is probably where we fill in the frags, just before
> > while (len)
> > 
> > The whole can only happen when mergeable buffers
> > are disabled, right?
> 
> >From what I understand it can happen whenever you're going to build a
> skb longer than PAGE_SIZE.

Hmm how exactly?  With mergeable buffers this only gets
the length of the 1st chunk which is up to 4K unless the driver
is buggy ...

> -- 
> 
> Sasha.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michael S. Tsirkin Sept. 26, 2011, 7:57 p.m. UTC | #5
On Mon, Sep 26, 2011 at 10:45:35PM +0300, Pekka Enberg wrote:
> On Mon, Sep 26, 2011 at 10:37 PM, Sasha Levin <levinsasha928@gmail.com> wrote:
> >> Interesting.  This is a theoretical issue, correct?
> >> Not a crash you actually see.
> >
> > Actually it was an actual crash caused when our virtio-net driver in kvm
> > tools did funny things and passed '(u32)-1' length as a buffer length to
> > the guest kernel.
> 
> I'm not sure what Michael means with "theoretical issue" here. Can the guest
> driver assume that the hypervisor doesn't attempt to do nasty things?
> 
>                           Pekka

IMO yes, hypervisor has full access to guest memory so it's a safe
assumption. But surviving in the face of hypervisor bugs
is laudable goal, bugs do happen.
Sasha Levin Sept. 26, 2011, 7:57 p.m. UTC | #6
On Mon, 2011-09-26 at 22:45 +0300, Pekka Enberg wrote:
> On Mon, Sep 26, 2011 at 10:37 PM, Sasha Levin <levinsasha928@gmail.com> wrote:
> >> Interesting.  This is a theoretical issue, correct?
> >> Not a crash you actually see.
> >
> > Actually it was an actual crash caused when our virtio-net driver in kvm
> > tools did funny things and passed '(u32)-1' length as a buffer length to
> > the guest kernel.
> 
> I'm not sure what Michael means with "theoretical issue" here. Can the guest
> driver assume that the hypervisor doesn't attempt to do nasty things?

afaik if the hypervisor can access the vcpus and the memory of the
guest, this shouldn't be a security issue - more of a bug prevention
issue.

I guess it'll be interesting the other way around, when it's the guest
that passes this buggy information to the hypervisor.
Pekka Enberg Sept. 26, 2011, 8:04 p.m. UTC | #7
On Mon, Sep 26, 2011 at 10:45:35PM +0300, Pekka Enberg wrote:
>> I'm not sure what Michael means with "theoretical issue" here. Can the guest
>> driver assume that the hypervisor doesn't attempt to do nasty things?

On Mon, Sep 26, 2011 at 10:57 PM, Michael S. Tsirkin <mst@redhat.com> wrote:
> IMO yes, hypervisor has full access to guest memory so it's a safe
> assumption. But surviving in the face of hypervisor bugs
> is laudable goal, bugs do happen.

I was thinking of a compromised guest that is able to trick the hypervisor into
doing nasty things to other guests without taking over the hypervisor
completely. So for something like virtio networking, that's by
definition exposed
to rest of the world, I think it's very important not to be robust
against hypervisor
bugs.

In any case, we were able to trigger this particular case rather easily with our
buggy tool, so it's definitely worth fixing. ;-)

FWIW,

Acked-by: Pekka Enberg <penberg@kernel.org>

                                Pekka
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sasha Levin Sept. 27, 2011, 6:44 a.m. UTC | #8
On Mon, 2011-09-26 at 22:55 +0300, Michael S. Tsirkin wrote:
> On Mon, Sep 26, 2011 at 10:37:22PM +0300, Sasha Levin wrote:
> > On Mon, 2011-09-26 at 21:44 +0300, Michael S. Tsirkin wrote:
> > > On Mon, Sep 26, 2011 at 08:41:08PM +0300, Sasha Levin wrote:
> > > > This patch verifies that the length of a buffer stored in a linked list
> > > > of pages is small enough to fit into a skb.
> > > > 
> > > > If the size is larger than a max size of a skb, it means that we shouldn't
> > > > go ahead building skbs anyway since we won't be able to send the buffer as
> > > > the user requested.
> > > > 
> > > > Cc: Rusty Russell <rusty@rustcorp.com.au>
> > > > Cc: "Michael S. Tsirkin" <mst@redhat.com>
> > > > Cc: virtualization@lists.linux-foundation.org
> > > > Cc: netdev@vger.kernel.org
> > > > Cc: kvm@vger.kernel.org
> > > > Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
> > > 
> > > Interesting.  This is a theoretical issue, correct?
> > > Not a crash you actually see.
> > 
> > Actually it was an actual crash caused when our virtio-net driver in kvm
> > tools did funny things and passed '(u32)-1' length as a buffer length to
> > the guest kernel.
> > 
> > > This crash would mean device is giving us packets
> > > that are way too large. Avoiding crashes even in the face of
> > > a misbehaved device is a good idea, but should
> > > we print a diagnostic to a system log?
> > > Maybe rate-limited or print once to avoid filling
> > > up the disk. Other places in driver print with pr_debug
> > > I'm not sure that's right but better than nothing.
> > 
> > Yup, I'll add some debug info.
> > 
> > > > ---
> > > >  drivers/net/virtio_net.c |    3 +++
> > > >  1 files changed, 3 insertions(+), 0 deletions(-)
> > > > 
> > > > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> > > > index 0c7321c..64e0717 100644
> > > > --- a/drivers/net/virtio_net.c
> > > > +++ b/drivers/net/virtio_net.c
> > > > @@ -165,6 +165,9 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi,
> > > >  	unsigned int copy, hdr_len, offset;
> > > >  	char *p;
> > > >  
> > > > +	if (len > MAX_SKB_FRAGS * PAGE_SIZE)
> > > 
> > > unlikely()?
> > > 
> > > Also, this seems too aggressive: at this point len includes the header
> > > and the linear part. The right place for this
> > > test is probably where we fill in the frags, just before
> > > while (len)
> > > 
> > > The whole can only happen when mergeable buffers
> > > are disabled, right?
> > 
> > >From what I understand it can happen whenever you're going to build a
> > skb longer than PAGE_SIZE.
> 
> Hmm how exactly?  With mergeable buffers this only gets
> the length of the 1st chunk which is up to 4K unless the driver
> is buggy ...

What about the case where TSO or ECN features are set? The code flow
suggests that mergeable would get ignored in that case:

	/* If we can receive ANY GSO packets, we must allocate large ones. */
	if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) ||
	    virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6) ||
	    virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN))
		vi->big_packets = true;

	[...]

	if (!vi->mergeable_rx_bufs && !vi->big_packets) {
		skb = buf;
		len -= sizeof(struct virtio_net_hdr);
		skb_trim(skb, len);
	} else {
		page = buf;
		skb = page_to_skb(vi, page, len);
		...


I haven't actually tested it with mergeable buffers enabled, but could
do it later today.
Michael S. Tsirkin Sept. 27, 2011, 7 a.m. UTC | #9
On Tue, Sep 27, 2011 at 09:44:02AM +0300, Sasha Levin wrote:
> On Mon, 2011-09-26 at 22:55 +0300, Michael S. Tsirkin wrote:
> > On Mon, Sep 26, 2011 at 10:37:22PM +0300, Sasha Levin wrote:
> > > On Mon, 2011-09-26 at 21:44 +0300, Michael S. Tsirkin wrote:
> > > > On Mon, Sep 26, 2011 at 08:41:08PM +0300, Sasha Levin wrote:
> > > > > This patch verifies that the length of a buffer stored in a linked list
> > > > > of pages is small enough to fit into a skb.
> > > > > 
> > > > > If the size is larger than a max size of a skb, it means that we shouldn't
> > > > > go ahead building skbs anyway since we won't be able to send the buffer as
> > > > > the user requested.
> > > > > 
> > > > > Cc: Rusty Russell <rusty@rustcorp.com.au>
> > > > > Cc: "Michael S. Tsirkin" <mst@redhat.com>
> > > > > Cc: virtualization@lists.linux-foundation.org
> > > > > Cc: netdev@vger.kernel.org
> > > > > Cc: kvm@vger.kernel.org
> > > > > Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
> > > > 
> > > > Interesting.  This is a theoretical issue, correct?
> > > > Not a crash you actually see.
> > > 
> > > Actually it was an actual crash caused when our virtio-net driver in kvm
> > > tools did funny things and passed '(u32)-1' length as a buffer length to
> > > the guest kernel.
> > > 
> > > > This crash would mean device is giving us packets
> > > > that are way too large. Avoiding crashes even in the face of
> > > > a misbehaved device is a good idea, but should
> > > > we print a diagnostic to a system log?
> > > > Maybe rate-limited or print once to avoid filling
> > > > up the disk. Other places in driver print with pr_debug
> > > > I'm not sure that's right but better than nothing.
> > > 
> > > Yup, I'll add some debug info.
> > > 
> > > > > ---
> > > > >  drivers/net/virtio_net.c |    3 +++
> > > > >  1 files changed, 3 insertions(+), 0 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> > > > > index 0c7321c..64e0717 100644
> > > > > --- a/drivers/net/virtio_net.c
> > > > > +++ b/drivers/net/virtio_net.c
> > > > > @@ -165,6 +165,9 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi,
> > > > >  	unsigned int copy, hdr_len, offset;
> > > > >  	char *p;
> > > > >  
> > > > > +	if (len > MAX_SKB_FRAGS * PAGE_SIZE)
> > > > 
> > > > unlikely()?
> > > > 
> > > > Also, this seems too aggressive: at this point len includes the header
> > > > and the linear part. The right place for this
> > > > test is probably where we fill in the frags, just before
> > > > while (len)
> > > > 
> > > > The whole can only happen when mergeable buffers
> > > > are disabled, right?
> > > 
> > > >From what I understand it can happen whenever you're going to build a
> > > skb longer than PAGE_SIZE.
> > 
> > Hmm how exactly?  With mergeable buffers this only gets
> > the length of the 1st chunk which is up to 4K unless the driver
> > is buggy ...
> 
> What about the case where TSO or ECN features are set? The code flow
> suggests that mergeable would get ignored in that case:
> 
> 	/* If we can receive ANY GSO packets, we must allocate large ones. */
> 	if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) ||
> 	    virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6) ||
> 	    virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN))
> 		vi->big_packets = true;
> 
> 	[...]
> 
> 	if (!vi->mergeable_rx_bufs && !vi->big_packets) {
> 		skb = buf;
> 		len -= sizeof(struct virtio_net_hdr);
> 		skb_trim(skb, len);
> 	} else {
> 		page = buf;
> 		skb = page_to_skb(vi, page, len);
> 		...

Sorry I don't get it yet. Where is mergeable ignored here?

> I haven't actually tested it with mergeable buffers enabled, but could
> do it later today.

Please do.

> -- 
> 
> Sasha.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sasha Levin Sept. 27, 2011, 11:20 a.m. UTC | #10
On Tue, 2011-09-27 at 10:00 +0300, Michael S. Tsirkin wrote:
> >               skb = page_to_skb(vi, page, len);
> >               ...
> 
> Sorry I don't get it yet. Where is mergeable ignored here?

The NULL deref happens in page_to_skb(), before merge buffers are
handled.

I'll test it and see if it's really the case.
Michael S. Tsirkin Sept. 27, 2011, 12:37 p.m. UTC | #11
On Tue, Sep 27, 2011 at 02:20:29PM +0300, Sasha Levin wrote:
> On Tue, 2011-09-27 at 10:00 +0300, Michael S. Tsirkin wrote:
> > >               skb = page_to_skb(vi, page, len);
> > >               ...
> > 
> > Sorry I don't get it yet. Where is mergeable ignored here?
> 
> The NULL deref happens in page_to_skb(), before merge buffers are
> handled.

The len here is a single buffer length, so for mergeable
buffers it is <= the size of the buffer we gave to hardware,
which is PAGE_SIZE.

        err = virtqueue_add_buf_single(vi->rvq, page_address(page),
                                       PAGE_SIZE, false, page, gfp);
 

Unless of course we are trying to work around broken hardware again,
which I don't have a problem with, but should probably
get appropriate comments in code and trigger a warning.

> I'll test it and see if it's really the case.
> 
> -- 
> 
> Sasha.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sasha Levin Sept. 28, 2011, 12:19 p.m. UTC | #12
On Tue, 2011-09-27 at 15:37 +0300, Michael S. Tsirkin wrote:
> On Tue, Sep 27, 2011 at 02:20:29PM +0300, Sasha Levin wrote:
> > On Tue, 2011-09-27 at 10:00 +0300, Michael S. Tsirkin wrote:
> > > >               skb = page_to_skb(vi, page, len);
> > > >               ...
> > > 
> > > Sorry I don't get it yet. Where is mergeable ignored here?
> > 
> > The NULL deref happens in page_to_skb(), before merge buffers are
> > handled.
> 
> The len here is a single buffer length, so for mergeable
> buffers it is <= the size of the buffer we gave to hardware,
> which is PAGE_SIZE.
> 
>         err = virtqueue_add_buf_single(vi->rvq, page_address(page),
>                                        PAGE_SIZE, false, page, gfp);
>  
> 
> Unless of course we are trying to work around broken hardware again,
> which I don't have a problem with, but should probably
> get appropriate comments in code and trigger a warning.
> 
> > I'll test it and see if it's really the case.

I've verified it with VIRTIO_NET_F_MRG_RXBUF set, and we still get the
NULL deref.
diff mbox

Patch

diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 0c7321c..64e0717 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -165,6 +165,9 @@  static struct sk_buff *page_to_skb(struct virtnet_info *vi,
 	unsigned int copy, hdr_len, offset;
 	char *p;
 
+	if (len > MAX_SKB_FRAGS * PAGE_SIZE)
+		return NULL;
+
 	p = page_address(page);
 
 	/* copy small packet so we can reuse these pages for small data */