[libvirt] [PATCH v2 1/2] Introduce Libvirt Wireshark dissector

Daniel P. Berrange berrange at redhat.com
Mon Sep 30 12:26:14 UTC 2013


On Mon, Sep 30, 2013 at 09:12:35PM +0900, Yuto KAWAMURA wrote:
> 2013/9/20 Daniel P. Berrange <berrange at redhat.com>:
> > On Thu, Sep 19, 2013 at 11:26:08PM +0900, Yuto KAWAMURA(kawamuray) wrote:
> >> diff --git a/tools/wireshark/src/moduleinfo.h b/tools/wireshark/src/moduleinfo.h
> >> new file mode 100644
> >> index 0000000..9ab642c
> >> --- /dev/null
> >> +++ b/tools/wireshark/src/moduleinfo.h
> >> @@ -0,0 +1,37 @@
> >> +/* moduleinfo.h --- Define constants about wireshark plugin module
> > ...
> >> +
> >> +/* Included *after* config.h, in order to re-define these macros */
> >> +
> >> +#ifdef PACKAGE
> >> +# undef PACKAGE
> >> +#endif
> >> +
> >> +/* Name of package */
> >> +#define PACKAGE "libvirt"
> >
> > Huh ?  "PACKAGE" will already be defined to 'libvirt' so why are
> > you redefining it.
> >
> >> +
> >> +
> >> +#ifdef VERSION
> >> +# undef VERSION
> >> +#endif
> >> +
> >> +/* Version number of package */
> >> +#define VERSION "0.0.1"
> >
> > This means the wireshark plugin will have a fixed version, even
> > when libvirt protocol changes in new releases. This seems bogus.
> > Again I think we should just use the existing defined "VERSION".
> >
> > I think this whole file can just go away completely
> >
> 
> Right. I'll remove whole moduleinfo.h.
> 
> >> diff --git a/tools/wireshark/src/packet-libvirt.c b/tools/wireshark/src/packet-libvirt.c
> >> new file mode 100644
> >> index 0000000..cd3e6ce
> >> --- /dev/null
> >> +++ b/tools/wireshark/src/packet-libvirt.c
> >> +static gboolean
> >> +dissect_xdr_bytes(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf,
> >> +                  guint32 maxlen)
> >> +{
> >> +    goffset start;
> >> +    guint8 *val = NULL;
> >> +    guint32 length;
> >> +
> >> +    start = xdr_getpos(xdrs);
> >> +    if (xdr_bytes(xdrs, (char **)&val, &length, maxlen)) {
> >> +        proto_tree_add_bytes_format_value(tree, hf, tvb, start, xdr_getpos(xdrs) - start,
> >> +                                          NULL, "%s", format_xdr_bytes(val, length));
> >> +        /* Seems I can't call xdr_free() for this case.
> >> +           It will raises SEGV by referencing out of bounds argument stack */
> >> +        xdrs->x_op = XDR_FREE;
> >> +        xdr_bytes(xdrs, (char **)&val, &length, maxlen);
> >> +        xdrs->x_op = XDR_DECODE;
> >
> > Is accessing the internals of the 'XDR' struct really portable ? I think
> > it would be desirable to solve the xdr_free problem rather than accessing
> > struct internals
> >
> 
> I'll change this to use free(), but let me explain this problem detail.
> 
> xdr_bytes may raises SEGV when it called from xdr_free.
> This is caused by xdr_free is accessing it's third argument 'sizep' even if
> it was called from xdr_free(in other word, when xdrs->x_op is XDR_FREE).
> This problem can't be reproduced in 64bit architecture due to 64bit
> system's register usage (I'll explain about this later).
> 
> Following is a small enough code to reproduce this issue:
> 
> #include <stdio.h>
> #include <stdlib.h>
> #include <rpc/xdr.h>
> 
> /* Contents of this buffer is not important to reproduce the issue */
> static char xdr_buffer[] = {
>     0x00, 0x00, 0x00, 0x02, /* length is 2byte */
>     'A', '\0', 0, 0         /* 2 byte data and 2 byte padding bytes */
> };
> 
> /* Same as the prototype of xdr_bytes() */
> bool_t my_xdr_bytes(XDR *xdrs, char **cpp, u_int *sizep)
> {
>     return TRUE;
> }
> 
> /* Same as the prototype of xdr_free() */
> void my_xdr_free(xdrproc_t proc, char *objp)
> {
>     XDR x;
>     (*proc)(&x, objp, NULL/* This NULL stored at same pos of 'sizep'
> in xdr_bytes() */);
> }
> 
> int main(void)
> {
>     XDR xdrs;
>     char *opaque = NULL;
>     unsigned int size;
> 
>     xdrmem_create(&xdrs, xdr_buffer, sizeof(xdr_buffer), XDR_DECODE);
>     if (!xdr_bytes(&xdrs, &opaque, &size, ~0)) {
>         fprintf(stderr, "xdr_bytes() returns FALSE\n");
>         exit(1);
>     }
> 
>     /* Reproduce same stack-upping as call of xdr_free(xdr_bytes, &opaque).
>        This is needed to stack-up 0x0(invalid address) on position of
>        'sizsp' which is third argument of xdr_bytes(). */
>     my_xdr_free((xdrproc_t)my_xdr_bytes, (char *)&opaque);
> 
>     /* *** SEGV!! *** */
>     xdr_free((xdrproc_t)xdr_bytes, (char *)&opaque);
>     /* ************** */

Ok, the scenario here is

 - 'xdr_bytes' takes 4 arguments
 - 'xdrproc_t' takes 2 mandatory args + var-args
 - 'xdr_free' calls the 'xdrproc_t' function with only 2 arguments
 - 'xdr_bytes' unconditionally accesses its 3rd argument

So either

 - the cast from xdr_bytes -> xdrproc_t is invalid and thus
   xdr_bytes should not be used with xdr_free.

or

 - xdr_bytes impl in glibc is buggy and shouldn't access the
   3rd arg except when doing encode/decode operations.

Regardless of which is right, we want our code to work on all xdr
impls, so we must avoid problem 2. So I think we should just not
use xdr_free here. Just do a plain 'free(opaque)' instead.

Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list