qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Eduardo Habkost <ehabkost@redhat.com>
To: "Philippe Mathieu-Daudé" <f4bug@amsat.org>
Cc: "Cleber Rosa" <crosa@redhat.com>,
	"Markus Armbruster" <armbru@redhat.com>,
	qemu-devel@nongnu.org, "Lukáš Doktor" <ldoktor@redhat.com>,
	"Daniel P . Berrangé" <berrange@redhat.com>
Subject: Re: [Qemu-devel] [RFC PATCH v2] qmp.py: Fix exception parsing partial JSON
Date: Wed, 6 Jun 2018 17:05:13 -0300	[thread overview]
Message-ID: <20180606200513.GY7451@localhost.localdomain> (raw)
In-Reply-To: <20180606192731.6910-1-f4bug@amsat.org>

On Wed, Jun 06, 2018 at 04:27:31PM -0300, Philippe Mathieu-Daudé wrote:
> The readline() call returns partial data.

How can this be reproduced?  Despite not being forbidden by the
QMP specification, QEMU normally doesn't break QMP replies in
multiple lines, and readline() is not supposed to return a
partial line unless it encounters EOF.


> Keep appending until the JSON buffer is complete.
> 
> This fixes:
> 
>     $ scripts/qmp/qmp-shell -v -p /tmp/qmp.sock
>     Traceback (most recent call last):
>       File "scripts/qmp/qmp-shell", line 456, in <module>
>         main()
>       File "scripts/qmp/qmp-shell", line 441, in main
>         qemu.connect(negotiate)
>       File "scripts/qmp/qmp-shell", line 284, in connect
>         self._greeting = super(QMPShell, self).connect(negotiate)
>       File "scripts/qmp/qmp.py", line 143, in connect
>         return self.__negotiate_capabilities()
>       File "scripts/qmp/qmp.py", line 71, in __negotiate_capabilities
>         greeting = self.__json_read()
>       File "scripts/qmp/qmp.py", line 85, in __json_read
>         resp = json.loads(data)
>       File "/usr/lib/python2.7/json/__init__.py", line 339, in loads
>         return _default_decoder.decode(s)
>       File "/usr/lib/python2.7/json/decoder.py", line 364, in decode
>         obj, end = self.raw_decode(s, idx=_w(s, 0).end())
>       File "/usr/lib/python2.7/json/decoder.py", line 380, in raw_decode
>         obj, end = self.scan_once(s, idx)
>     ValueError: Expecting object: line 1 column 3 (char 2)
> 
> Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> ---
> Since v1:
> - addressed Daniel review: clean data after json.loads() succeeds
> - add a XXX comment
> 
> Daniel suggested this is due to blocking i/o.
> 
> I'm sure there is a nicer/more pythonic way to do this, but this works for me,
> sorry :)

It looks like there's no elegant solution for this:
https://stackoverflow.com/a/21709058

> 
>  scripts/qmp/qmp.py | 13 ++++++++++---
>  1 file changed, 10 insertions(+), 3 deletions(-)
> 
> diff --git a/scripts/qmp/qmp.py b/scripts/qmp/qmp.py
> index 5c8cf6a056..14f0b48936 100644
> --- a/scripts/qmp/qmp.py
> +++ b/scripts/qmp/qmp.py
> @@ -78,11 +78,18 @@ class QEMUMonitorProtocol(object):
>          raise QMPCapabilitiesError
>  
>      def __json_read(self, only_event=False):
> +        data = ""
>          while True:
> -            data = self.__sockfile.readline()
> -            if not data:
> +            tmp = self.__sockfile.readline()
> +            if not tmp:
>                  return
> -            resp = json.loads(data)
> +            data += tmp
> +            try:
> +                resp = json.loads(data)
> +            except ValueError:
> +                # XXX: blindly loop, even if QEMU ever sends malformed data
> +                continue

I was going to suggest using json.JSONDecoder.raw_decode() and
saving the remaining data in case we already read partial data
for a second JSON message.

But the QMP specification says all messages are terminated with
CRLF, so we we should never see the data for two different
messages in a single readline() call.  Noting this in a comment
wouldn't hurt, though.

The patch seems reasonable, but first I would like to understand
how this bug can be triggered.

> +            data = ""
>              if 'event' in resp:
>                  self.logger.debug("<<< %s", resp)
>                  self.__events.append(resp)
> -- 
> 2.17.1
> 

-- 
Eduardo

  reply	other threads:[~2018-06-06 20:05 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-06-06 19:27 [Qemu-devel] [RFC PATCH v2] qmp.py: Fix exception parsing partial JSON Philippe Mathieu-Daudé
2018-06-06 20:05 ` Eduardo Habkost [this message]
2018-06-06 23:06   ` Philippe Mathieu-Daudé
2018-06-08 17:57     ` Lukáš Doktor
2018-06-08 18:01       ` Eduardo Habkost

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180606200513.GY7451@localhost.localdomain \
    --to=ehabkost@redhat.com \
    --cc=armbru@redhat.com \
    --cc=berrange@redhat.com \
    --cc=crosa@redhat.com \
    --cc=f4bug@amsat.org \
    --cc=ldoktor@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).