virtio-serial: Trivial fixes, don't copy buffers to host
cover virtio-serial: Trivial fixes, don't copy buffers to host
Hi,
This patch series converts virtio-serial-bus to use the guest buffers instead of copying over guest data to the host, as suggested by Paul.
In addition, there are some trivial fixes for the virtio-console and virtio-serial code.
Amit Shah (5):
virtio-console: Factor out common init between console and generic
ports
virtio-console: Remove unnecessary braces
virtio-serial: Simplify condition for a while loop
virtio-serial: Don't copy over guest buffer to host
virtio-serial: Error out if guest sends unexpected vq elements
hw/virtio-console.c | 34 +++++++++++++++------------------- hw/virtio-serial-bus.c | 44 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 28 deletions(-)
Signature · 1 line
1.7.3.2
PATCH 1/5 virtio-console: Factor out common init between console and generic ports
The initialisation for generic ports and console ports is similar. Factor out the parts that are the same in a different function that can be called from each of the initfns.
--- hw/virtio-console.c | 31 ++++++++++++++----------------- 1 files changed, 14 insertions(+), 17 deletions(-)
hw/virtio-console.c +14 -17
@@ -58,24 +58,28 @@ static void chr_event(void *opaque, int event) } } -/* Virtio Console Ports */ -static int virtconsole_initfn (VirtIOSerialDevice *dev) +static int generic_port_init (VirtConsole *vcon,VirtIOSerialDevice *dev) { -VirtIOSerialPort *port =DO_UPCAST (VirtIOSerialPort, dev, &dev->qdev); -VirtConsole *vcon =DO_UPCAST (VirtConsole, port, port); - - port->info = dev->info; - - port->is_console = true; + vcon->port.info = dev->info;if (vcon->chr) {qemu_chr_add_handlers (vcon->chr, chr_can_read, chr_read, chr_event, vcon); - port->info->have_data = flush_buf; + vcon->port.info->have_data = flush_buf; }return 0 ; } +/* Virtio Console Ports */ +static int virtconsole_initfn (VirtIOSerialDevice *dev) +{ +VirtIOSerialPort *port =DO_UPCAST (VirtIOSerialPort, dev, &dev->qdev); +VirtConsole *vcon =DO_UPCAST (VirtConsole, port, port); + + port->is_console = true; +return generic_port_init (vcon, dev); +} +static int virtconsole_exitfn (VirtIOSerialDevice *dev) {VirtIOSerialPort *port =DO_UPCAST (VirtIOSerialPort, dev, &dev->qdev);
@@ -115,14 +119,7 @@ static int virtserialport_initfn(VirtIOSerialDevice *dev)VirtIOSerialPort *port =DO_UPCAST (VirtIOSerialPort, dev, &dev->qdev);VirtConsole *vcon =DO_UPCAST (VirtConsole, port, port); - port->info = dev->info; - -if (vcon->chr) { -qemu_chr_add_handlers (vcon->chr, chr_can_read, chr_read, chr_event, - vcon); - port->info->have_data = flush_buf; - } -return 0 ; +return generic_port_init (vcon, dev); }static VirtIOSerialPortInfo virtserialport_info = {
Signature · 1 line
1.7.3.2
PATCH 2/5 virtio-console: Remove unnecessary braces
In reply to Amit ShahRemove unnecessary braces around a case statement.
--- hw/virtio-console.c | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-)
hw/virtio-console.c +1 -2
@@ -48,10 +48,9 @@ static void chr_event(void *opaque, int event)VirtConsole *vcon = opaque;switch (event) { -case CHR_EVENT_OPENED : { +case CHR_EVENT_OPENED :virtio_serial_open (&vcon->port);break ; - }case CHR_EVENT_CLOSED :virtio_serial_close (&vcon->port);break ;
Signature · 1 line
1.7.3.2
PATCH 3/5 virtio-serial: Simplify condition for a while loop
In reply to Amit ShahSeparate out a non-changing condition over the period of a loop into an if statement before the loop. This will be used later to re-work the loop.
--- hw/virtio-serial-bus.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-)
hw/virtio-serial-bus.c +4 -1
@@ -121,7 +121,10 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,assert (port || discard);assert (virtio_queue_ready (vq)); -while ((discard || !port->throttled) &&virtqueue_pop (vq, &elem)) { +if (!discard && port->throttled) { +return ; + } +while (virtqueue_pop (vq, &elem)) {uint8_t *buf;size_t ret, buf_size;
Signature · 1 line
1.7.3.2
PATCH 4/5 virtio-serial: Don't copy over guest buffer to host
In reply to Amit ShahWhen the guest writes something to a host, we copied over the entire buffer first into the host and then processed it. Do away with that, it could result in a malicious guest causing a DoS on the host.
--- hw/virtio-serial-bus.c | 20 ++++++++++++-------- 1 files changed, 12 insertions(+), 8 deletions(-)
hw/virtio-serial-bus.c +12 -8
@@ -125,17 +125,21 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,return ; }while (virtqueue_pop (vq, &elem)) { -uint8_t *buf; -size_t ret, buf_size; +unsigned int i; -if (!discard) { - buf_size =iov_size (elem.out_sg, elem.out_num); - buf =qemu_malloc (buf_size); - ret =iov_to_buf (elem.out_sg, elem.out_num, buf,0 , buf_size); +if (discard) { + goto next; + } +for (i =0 ; i < elem.out_num; i++) { +size_t buf_size; - port->info->have_data (port, buf, ret); -qemu_free (buf); + buf_size = elem.out_sg[i].iov_len; + + port->info->have_data (port, + elem.out_sg[i].iov_base, + buf_size); } + next:virtqueue_push (vq, &elem,0 ); }virtio_notify (vdev, vq);
Signature · 1 line
1.7.3.2
[Qemu-devel] Re: [PATCH 4/5] virtio-serial: Don't copy over guest buffer to host
hw/virtio-serial-bus.c static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, · line 143
Quoted 7 lines
hw/virtio-serial-bus.c +4
-1-if (!discard) { +if (discard) { + goto next; + } + next:virtqueue_push (vq, &elem,0 );
Please don't do this.
Paul
[Qemu-devel] Re: [PATCH 4/5] virtio-serial: Don't copy over guest buffer to host
Quoted 9 lines · Paul Brook
hw/virtio-serial-bus.c static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, · line 143
Quoted 7 lines
hw/virtio-serial-bus.c +4
-1-if (!discard) { +if (discard) { + goto next; + } + next:virtqueue_push (vq, &elem,0 );Please don't do this.
Could you elaborate?
I can move the 'discard' check into the following 'for' loop, but since the value of discard doesn't change, I moved it outside.
Amit
[Qemu-devel] Re: [PATCH 4/5] virtio-serial: Don't copy over guest buffer to host
Quoted 9 lines · Paul Brook
Quoted 8 lines
Quoted 7 lines
hw/virtio-serial-bus.c +4
-1-if (!discard) { +if (discard) { + goto next; + } + next:virtqueue_push (vq, &elem,0 );
Quoted 6 lines
Quoted 1 line
Please don't do this.
Could you elaborate?
I can move the 'discard' check into the following 'for' loop, but since the value of discard doesn't change, I moved it outside.
You've replaced a perfectly good if block with a goto.
Paul
[Qemu-devel] Re: [PATCH 4/5] virtio-serial: Don't copy over guest buffer to host
Quoted 9 lines · Paul Brook
Quoted 9 lines · Paul Brook
Quoted 8 lines
Quoted 7 lines
hw/virtio-serial-bus.c +4
-1-if (!discard) { +if (discard) { + goto next; + } + next:virtqueue_push (vq, &elem,0 );
Quoted 8 lines
Quoted 6 lines
Quoted 1 line
Please don't do this.
Could you elaborate?
I can move the 'discard' check into the following 'for' loop, but since the value of discard doesn't change, I moved it outside.
You've replaced a perfectly good if block with a goto.
To keep the indentation levels low.
I've put it in the for loop for v2.
Amit
PATCH 5/5 virtio-serial: Error out if guest sends unexpected vq elements
In reply to Amit ShahCheck if the guest really sent any items in the out_vq before using them. Similarly, check if there is a buffer to send data in before writing.
--- hw/virtio-serial-bus.c | 19 +++++++++++++++++++ 1 files changed, 19 insertions(+), 0 deletions(-)
hw/virtio-serial-bus.c +19 -0
@@ -102,6 +102,11 @@ static size_t write_to_port(VirtIOSerialPort *port,break ; } +if (elem.in_num <1 ) { +error_report ("No buffer to send data in?" ); +abort (); + } + len =iov_from_buf (elem.in_sg, elem.in_num, buf + offset, size - offset); offset += len;
@@ -127,6 +132,11 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,while (virtqueue_pop (vq, &elem)) {unsigned int i; +if (elem.out_num <1 ) { +error_report ("No data sent by guest?" ); +abort (); + } +if (discard) { goto next; }
@@ -169,6 +179,11 @@ static size_t send_control_msg(VirtIOSerialPort *port, void *buf, size_t len)return 0 ; } +if (elem.in_num <1 ) { +error_report ("No buffer to send control data in?" ); +abort (); + } + cpkt = (struct virtio_console_control *)buf;stl_p (&cpkt->id, port->id);memcpy (elem.in_sg[0 ].iov_base, buf, len);
@@ -386,6 +401,10 @@ static void control_out(VirtIODevice *vdev, VirtQueue *vq)while (virtqueue_pop (vq, &elem)) {size_t cur_len, copied; +if (elem.out_num <1 ) { +error_report ("No data sent in control packet" ); +abort (); + } cur_len =iov_size (elem.out_sg, elem.out_num);/* * Allocate a new buf only if we didn't have one previously or
Signature · 1 line
1.7.3.2
[Qemu-devel] Re: [PATCH 5/5] virtio-serial: Error out if guest sends unexpected vq elements
Quoted 3 lines
Check if the guest really sent any items in the
out_vqbefore using them. Similarly, check if there is a buffer to send data in before writing.
Can this actually happen? If so why/how?
Why does it need a special case in this device?
If this is guest triggerable then calling abort() is wrong.
Paul
[Qemu-devel] Re: [PATCH 5/5] virtio-serial: Error out if guest sends unexpected vq elements
Quoted 6 lines · Paul Brook
Quoted 3 lines
Check if the guest really sent any items in the
out_vqbefore using them. Similarly, check if there is a buffer to send data in before writing.Can this actually happen? If so why/how?
Why does it need a special case in this device?
A malicious guest (ie, a guest with the virtio_console module suitably modified) could send in buffers with the 'input' bit set instead of output as expected or vice-versa.
Quoted 1 line
If this is guest triggerable then calling
abort()is wrong.
It's either a guest bug or a malicious guest. What action is recommended?
Amit
[Qemu-devel] Re: [PATCH 5/5] virtio-serial: Error out if guest sends unexpected vq elements
Quoted 11 lines · Paul Brook
Quoted 6 lines
Quoted 3 lines
Check if the guest really sent any items in the
out_vqbefore using them. Similarly, check if there is a buffer to send data in before writing.Can this actually happen? If so why/how?
Why does it need a special case in this device?A malicious guest (ie, a guest with the
virtio_consolemodule suitably modified) could send in buffers with the 'input' bit set instead of output as expected or vice-versa.
So what? Who cares if they get it wrong?
It's entirely unclear whether this is actually an error. If a request has zero size then we just transfer zero bytes, exactly as requested.
Even if you accept this should be a diagnosable error, I suspect your patch is still insufficient. I don't see any code to check that input queue requests have zero output segments, nor do I see anything to handle zero-length segments.
Quoted 4 lines
Quoted 1 line
If this is guest triggerable then calling
abort()is wrong.It's either a guest bug or a malicious guest. What action is recommended?
Killing the whole VM in response to a malformed request to a device is clearly a bug in that device. You should report an error to the guest in the normal manner. IIRC virtio lacks any consistent error reporting mechanisms, and the usual response when asked to do something impossible is to reset the device.
Paul
[Qemu-devel] Re: [PATCH 5/5] virtio-serial: Error out if guest sends unexpected vq elements
Quoted 5 lines · Paul Brook
Quoted 5 lines · Paul Brook
Quoted 4 lines
Quoted 3 lines
Check if the guest really sent any items in the
out_vqbefore using them. Similarly, check if there is a buffer to send data in before writing.
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
Can this actually happen? If so why/how?
Why does it need a special case in this device?A malicious guest (ie, a guest with the
virtio_consolemodule suitably modified) could send in buffers with the 'input' bit set instead of output as expected or vice-versa.So what? Who cares if they get it wrong?
Just let the error_report() be there and continue as if nothing happened?
Quoted 7 lines
It's entirely unclear whether this is actually an error. If a request has zero size then we just transfer zero bytes, exactly as requested.
Even if you accept this should be a diagnosable error, I suspect your patch is still insufficient. I don't see any code to check that input queue requests have zero output segments, nor do I see anything to handle zero-length segments.
virtio actually supports sending both, input as well as output types of buffers in one go.
Quoted 9 lines
Quoted 4 lines
Quoted 1 line
If this is guest triggerable then calling
abort()is wrong.It's either a guest bug or a malicious guest. What action is recommended?
Killing the whole VM in response to a malformed request to a device is clearly a bug in that device. You should report an error to the guest in the normal manner. IIRC virtio lacks any consistent error reporting mechanisms, and the usual response when asked to do something impossible is to reset the device.
OK, agreed.
Amit