mbox series

[v3,00/25] chardev: Convert qemu_chr_write() to take a size_t argument

Message ID 20190220010232.18731-1-philmd@redhat.com (mailing list archive)
Headers show
Series chardev: Convert qemu_chr_write() to take a size_t argument | expand

Message

Philippe Mathieu-Daudé Feb. 20, 2019, 1:02 a.m. UTC
Hi,

This series convert the chardev::qemu_chr_write() to take unsigned
length argument. To do so I went through all caller and checked if
there are no negative value possible.

I'm having headaches with the Xen backend, talking with Marc-André
he suggested I ask help to the Xen maintainers.

Since the series is becoming quite big, I splitted it:
- part 1: convert qemu_chr_write()
- part 2: convert IOReadHandler

part 1 can be reviewed and merged without part 2.

The git diffstat is not huge, but there are various chardev subsystems
so many maintainers to ask for Ack.

v2: https://lists.gnu.org/archive/html/qemu-devel/2018-10/msg02396.html
v1: https://lists.gnu.org/archive/html/qemu-devel/2018-10/msg02200.html
    (from Prasad J Pandit)
Changes requested by Paolo:
https://lists.gnu.org/archive/html/qemu-devel/2018-10/msg02294.html

Please review,

Phil.

Philippe Mathieu-Daudé (25):
  chardev: Simplify IOWatchPoll::fd_can_read as a GSourceFunc
  chardev: Assert IOCanReadHandler can not be negative
  chardev/wctablet: Use unsigned type to hold unsigned value
  chardev: Let qemu_chr_be_can_write() return a size_t types
  gdbstub: Use size_t for strlen() return value
  gdbstub: Use size_t to hold GDBState::last_packet_len
  gdbstub: Let put_buffer() use size_t
  ui/gtk: Remove pointless cast
  vhost-user: Express sizeof with size_t
  usb-redir: Verify usbredirparser_write get called with positive count
  xen: Let xencons_send() take a 'size' argument
  xen: Let buffer_append() return the size consumed
  RFC xen: Let buffer_append() return a size_t
  virtio-serial: Let VirtIOSerialPortClass::have_data() use size_t
  spapr-vty: Let vty_putchars() use size_t
  tpm: Use size_t to hold sizes
  net/filter-mirror: Use size_t
  s390x/3270: Let insert_IAC_escape_char() use size_t
  s390/ebcdic: Use size_t to iterate over arrays
  s390x/sclp: Use a const variable to improve readability
  s390x/sclp: Use size_t in process_mdb()
  s390x/sclp: Let write_console_data() take a size_t
  hw/ipmi: Assert outlen > outpos
  chardev: Let qemu_chr_fe_write[_all] use size_t type argument
  chardev: Let qemu_chr_write[_all] use size_t

 chardev/baum.c                    |  6 +++---
 chardev/char-fd.c                 |  6 +++---
 chardev/char-fe.c                 |  4 ++--
 chardev/char-io.c                 |  6 +++---
 chardev/char-mux.c                |  3 ++-
 chardev/char-pty.c                |  8 ++++----
 chardev/char-socket.c             | 13 +++++++------
 chardev/char-udp.c                |  8 ++++----
 chardev/char-win.c                |  2 +-
 chardev/char.c                    | 15 +++++++++------
 chardev/msmouse.c                 |  4 ++--
 chardev/spice.c                   |  2 +-
 chardev/trace-events              |  2 +-
 chardev/wctablet.c                |  9 +++++----
 gdbstub.c                         |  8 ++++----
 hw/bt/hci-csr.c                   |  2 +-
 hw/char/sclpconsole-lm.c          | 12 +++++++-----
 hw/char/spapr_vty.c               |  2 +-
 hw/char/terminal3270.c            |  7 ++++---
 hw/char/virtio-console.c          |  2 +-
 hw/char/xen_console.c             | 24 +++++++++++++++---------
 hw/ipmi/ipmi_bmc_extern.c         |  3 ++-
 hw/tpm/tpm_emulator.c             |  7 ++++---
 hw/usb/redirect.c                 |  6 +++++-
 hw/virtio/vhost-user.c            | 14 ++++++++------
 include/chardev/char-fd.h         |  2 +-
 include/chardev/char-fe.h         |  4 ++--
 include/chardev/char-io.h         |  2 +-
 include/chardev/char.h            |  4 ++--
 include/hw/ppc/spapr_vio.h        |  2 +-
 include/hw/s390x/ebcdic.h         |  8 ++++----
 include/hw/virtio/virtio-serial.h |  2 +-
 include/sysemu/replay.h           |  2 +-
 net/filter-mirror.c               |  2 +-
 replay/replay-char.c              |  2 +-
 stubs/replay.c                    |  2 +-
 ui/console.c                      |  6 +++---
 ui/gtk.c                          |  2 +-
 38 files changed, 119 insertions(+), 96 deletions(-)

Comments

Marc-André Lureau Feb. 20, 2019, 10:53 a.m. UTC | #1
Hi

On Wed, Feb 20, 2019 at 2:02 AM Philippe Mathieu-Daudé
<philmd@redhat.com> wrote:
>
> Hi,
>
> This series convert the chardev::qemu_chr_write() to take unsigned
> length argument. To do so I went through all caller and checked if
> there are no negative value possible.


Changing signedness is problematic and can easily introduce bugs that
are easy to miss during review.

I agree with Cornelia about idiomatic use of int. Changing "int" for
"size_t" isn't systematically a clear win.

Even Google C++ style recommends to avoid unsigned types "(except for
representing bitfields or modular arithmetic). Do not use an unsigned
type merely to assert that a variable is non-negative."
https://google.github.io/styleguide/cppguide.html#Integer_Types - see rationale

Since Paolo you suggested the change, could you give some convincing
arguments that it's worth taking the plunge?

thanks

>
> I'm having headaches with the Xen backend, talking with Marc-André
> he suggested I ask help to the Xen maintainers.
>
> Since the series is becoming quite big, I splitted it:
> - part 1: convert qemu_chr_write()
> - part 2: convert IOReadHandler
>
> part 1 can be reviewed and merged without part 2.
>
> The git diffstat is not huge, but there are various chardev subsystems
> so many maintainers to ask for Ack.
>
> v2: https://lists.gnu.org/archive/html/qemu-devel/2018-10/msg02396.html
> v1: https://lists.gnu.org/archive/html/qemu-devel/2018-10/msg02200.html
>     (from Prasad J Pandit)
> Changes requested by Paolo:
> https://lists.gnu.org/archive/html/qemu-devel/2018-10/msg02294.html
>
> Please review,
>
> Phil.
>
> Philippe Mathieu-Daudé (25):
>   chardev: Simplify IOWatchPoll::fd_can_read as a GSourceFunc
>   chardev: Assert IOCanReadHandler can not be negative
>   chardev/wctablet: Use unsigned type to hold unsigned value
>   chardev: Let qemu_chr_be_can_write() return a size_t types
>   gdbstub: Use size_t for strlen() return value
>   gdbstub: Use size_t to hold GDBState::last_packet_len
>   gdbstub: Let put_buffer() use size_t
>   ui/gtk: Remove pointless cast
>   vhost-user: Express sizeof with size_t
>   usb-redir: Verify usbredirparser_write get called with positive count
>   xen: Let xencons_send() take a 'size' argument
>   xen: Let buffer_append() return the size consumed
>   RFC xen: Let buffer_append() return a size_t
>   virtio-serial: Let VirtIOSerialPortClass::have_data() use size_t
>   spapr-vty: Let vty_putchars() use size_t
>   tpm: Use size_t to hold sizes
>   net/filter-mirror: Use size_t
>   s390x/3270: Let insert_IAC_escape_char() use size_t
>   s390/ebcdic: Use size_t to iterate over arrays
>   s390x/sclp: Use a const variable to improve readability
>   s390x/sclp: Use size_t in process_mdb()
>   s390x/sclp: Let write_console_data() take a size_t
>   hw/ipmi: Assert outlen > outpos
>   chardev: Let qemu_chr_fe_write[_all] use size_t type argument
>   chardev: Let qemu_chr_write[_all] use size_t
>
>  chardev/baum.c                    |  6 +++---
>  chardev/char-fd.c                 |  6 +++---
>  chardev/char-fe.c                 |  4 ++--
>  chardev/char-io.c                 |  6 +++---
>  chardev/char-mux.c                |  3 ++-
>  chardev/char-pty.c                |  8 ++++----
>  chardev/char-socket.c             | 13 +++++++------
>  chardev/char-udp.c                |  8 ++++----
>  chardev/char-win.c                |  2 +-
>  chardev/char.c                    | 15 +++++++++------
>  chardev/msmouse.c                 |  4 ++--
>  chardev/spice.c                   |  2 +-
>  chardev/trace-events              |  2 +-
>  chardev/wctablet.c                |  9 +++++----
>  gdbstub.c                         |  8 ++++----
>  hw/bt/hci-csr.c                   |  2 +-
>  hw/char/sclpconsole-lm.c          | 12 +++++++-----
>  hw/char/spapr_vty.c               |  2 +-
>  hw/char/terminal3270.c            |  7 ++++---
>  hw/char/virtio-console.c          |  2 +-
>  hw/char/xen_console.c             | 24 +++++++++++++++---------
>  hw/ipmi/ipmi_bmc_extern.c         |  3 ++-
>  hw/tpm/tpm_emulator.c             |  7 ++++---
>  hw/usb/redirect.c                 |  6 +++++-
>  hw/virtio/vhost-user.c            | 14 ++++++++------
>  include/chardev/char-fd.h         |  2 +-
>  include/chardev/char-fe.h         |  4 ++--
>  include/chardev/char-io.h         |  2 +-
>  include/chardev/char.h            |  4 ++--
>  include/hw/ppc/spapr_vio.h        |  2 +-
>  include/hw/s390x/ebcdic.h         |  8 ++++----
>  include/hw/virtio/virtio-serial.h |  2 +-
>  include/sysemu/replay.h           |  2 +-
>  net/filter-mirror.c               |  2 +-
>  replay/replay-char.c              |  2 +-
>  stubs/replay.c                    |  2 +-
>  ui/console.c                      |  6 +++---
>  ui/gtk.c                          |  2 +-
>  38 files changed, 119 insertions(+), 96 deletions(-)
>
> --
> 2.20.1
>
Cornelia Huck Feb. 20, 2019, 10:57 a.m. UTC | #2
On Wed, 20 Feb 2019 11:53:42 +0100
Marc-André Lureau <marcandre.lureau@redhat.com> wrote:

> Hi
> 
> On Wed, Feb 20, 2019 at 2:02 AM Philippe Mathieu-Daudé
> <philmd@redhat.com> wrote:
> >
> > Hi,
> >
> > This series convert the chardev::qemu_chr_write() to take unsigned
> > length argument. To do so I went through all caller and checked if
> > there are no negative value possible.  
> 
> 
> Changing signedness is problematic and can easily introduce bugs that
> are easy to miss during review.
> 
> I agree with Cornelia about idiomatic use of int. Changing "int" for
> "size_t" isn't systematically a clear win.
> 
> Even Google C++ style recommends to avoid unsigned types "(except for
> representing bitfields or modular arithmetic). Do not use an unsigned
> type merely to assert that a variable is non-negative."
> https://google.github.io/styleguide/cppguide.html#Integer_Types - see rationale
> 
> Since Paolo you suggested the change, could you give some convincing
> arguments that it's worth taking the plunge?

FWIW, using an explicitly unsigned type for a length sounds fine; but
not all conversions are really convincing (albeit not wrong).
Daniel P. Berrangé Feb. 20, 2019, 11:30 a.m. UTC | #3
On Wed, Feb 20, 2019 at 11:53:42AM +0100, Marc-André Lureau wrote:
> Hi
> 
> On Wed, Feb 20, 2019 at 2:02 AM Philippe Mathieu-Daudé
> <philmd@redhat.com> wrote:
> >
> > Hi,
> >
> > This series convert the chardev::qemu_chr_write() to take unsigned
> > length argument. To do so I went through all caller and checked if
> > there are no negative value possible.
> 
> 
> Changing signedness is problematic and can easily introduce bugs that
> are easy to miss during review.
> 
> I agree with Cornelia about idiomatic use of int. Changing "int" for
> "size_t" isn't systematically a clear win.
> 
> Even Google C++ style recommends to avoid unsigned types "(except for
> representing bitfields or modular arithmetic). Do not use an unsigned
> type merely to assert that a variable is non-negative."
> https://google.github.io/styleguide/cppguide.html#Integer_Types - see rationale
> 
> Since Paolo you suggested the change, could you give some convincing
> arguments that it's worth taking the plunge?

The chardev write/read methods will end up calling libc read/write
methods, whose parameters are "size_t count".

Thus if there is QEMU code that could currently (mistakenly) pass a
negative value for length to qemu_chr_write, unless something stops
it, this is going to be cast to a size_t when we finally call read/
write on the FD, leading to a large positive value & array out of
bounds read/write. 

IOW we already have inconsistent use of signed vs unsigned in our code
which has potential to cause bugs. Converting chardev to use size_t
we get rid fo the mismatch with the underlying libc APIs we call,
which ultimately eliminates an area of risk longer term. There is a
chance it could uncover some pre-existing dormant bugs, but provided
we do due diligence to check callers I think its a win to be consistent
with libc APIs in size_t usage for read/write.

Regards,
Daniel
Eric Blake Feb. 20, 2019, 2:20 p.m. UTC | #4
On 2/20/19 5:30 AM, Daniel P. Berrangé wrote:

>> Since Paolo you suggested the change, could you give some convincing
>> arguments that it's worth taking the plunge?
> 
> The chardev write/read methods will end up calling libc read/write
> methods, whose parameters are "size_t count".

In my mind, that's the convincing reason. We should model our read/write
after the libc read/write, which means size_t input and ssize_t returns.

> 
> Thus if there is QEMU code that could currently (mistakenly) pass a
> negative value for length to qemu_chr_write, unless something stops
> it, this is going to be cast to a size_t when we finally call read/
> write on the FD, leading to a large positive value & array out of
> bounds read/write. 
> 
> IOW we already have inconsistent use of signed vs unsigned in our code
> which has potential to cause bugs. Converting chardev to use size_t
> we get rid fo the mismatch with the underlying libc APIs we call,
> which ultimately eliminates an area of risk longer term. There is a
> chance it could uncover some pre-existing dormant bugs, but provided
> we do due diligence to check callers I think its a win to be consistent
> with libc APIs in size_t usage for read/write.

And hopefully this exercise of making the conversion serves as a good
audit to help us gain confidence in our code and/or fix bugs it uncovers.