[libvirt] [PATCH] Santize the reporting of VIR_ERR_INVALID_ERROR

Daniel P. Berrange berrange at redhat.com
Mon May 28 09:53:23 UTC 2012


On Fri, May 25, 2012 at 05:35:20PM -0600, Eric Blake wrote:
> > +# define virReportInvalidNullArg(argname)                            \
> > +    virRaiseErrorFull(__FILE__, __FUNCTION__, __LINE__,              \
> > +                      VIR_FROM_THIS,                                 \
> > +                      VIR_ERR_INVALID_ARG,                           \
> > +                      VIR_ERR_ERROR,                                 \
> > +                      __FUNCTION__,                                  \
> > +                      #argname,                                      \
> > +                      NULL,                                          \
> > +                      0, 0,                                          \
> > +                      _("%s in %s must be NULL"),                    \
> > +                      #argname, __FUNCTION__)
> 
> Still feels a little redundant to report __FUNCTION__ in both the
> location and in the message, but at least the message is sane and by
> funnelling all clients through this one point you can change the message
> in the future with a lot less effort.  I can live with it.

Although when we send the error message onto virLogMessage(), the
function name will be prepended to the error string, if the application
has installed a custom handler for virErrorPtr they may well only be
printing out the error message. Thus I want to make sure that the error
message is "self contained" and not rely on people printing out other
data fields from the virErrorPtr.

> > +# define virReportInvalidArg(argname, fmt, ...)                      \
> > +    virRaiseErrorFull(__FILE__, __FUNCTION__, __LINE__,              \
> > +                      VIR_FROM_THIS,                                 \
> > +                      VIR_ERR_INVALID_ARG,                           \
> > +                      VIR_ERR_ERROR,                                 \
> > +                      __FUNCTION__,                                  \
> > +                      #argname,                                      \
> > +                      NULL,                                          \
> > +                      0, 0,                                          \
> > +                      (fmt), __VA_ARGS__)
> 
> This requires a fmt argument and subsequent arguments, probably a good
> thing since you aren't using it for a canned message.
> 
> Is the idea that down the road you will add a syntax check that forbids
> raw use of VIR_ERR_INVALID_ARG, and must instead use
> virReportInvalidArg() or a better error category?

Yes, I think we should deny direct use of VIR_ERR_INVALID_ARG once
this conversion is complete.

> >              return retval;                                              \
> >          }                                                               \
> >      } while (0)
> >
> > +# define virCheckNonNullArgReturn(argname, retval)  \
> > +    do {                                            \
> > +        if (argname == NULL) {                      \
> > +            virReportInvalidNullArg(#argname);      \
> 
> Hmm.  This stringizes 'argname', but then virReportInvalidNullArg() also
> stringizes its argument.  Does that mean we are actually generating
> messages with literal quotes injected due to the double stringize?
> func: "arg" in func must not be NULL

That double stringizing is a mistake.

> > @@ -813,7 +790,7 @@ virUnrefStorageVol(virStorageVolPtr vol) {
> >      int refs;
> >  
> >      if (!VIR_IS_CONNECTED_STORAGE_VOL(vol)) {
> > -        virLibConnError(VIR_ERR_INVALID_ARG,
> > +        virLibConnError(VIR_ERR_INVALID_STORAGE_VOL,
> >                          _("bad storage volume or no connection"));
> >          return -1;
> >      }
> 
> I'm wondering if a future patch should factor out these checks for valid
> object into a one-liner as well, such as virCheckStorageVolReturn(vol,
> NULL).  But doesn't need to be done for the current patch.

Yes, shortening this is definitely my intention.

> > +++ b/src/libvirt.c
> 
> > @@ -746,12 +725,6 @@ virRegisterDriver(virDriverPtr driver)
> >          return -1;
> >      }
> >  
> > -    if (driver->no < 0) {
> > -        virLibConnError(VIR_ERR_INVALID_ARG,
> > -                        _("Tried to register an internal Xen driver"));
> > -        return -1;
> > -    }
> > -
> 
> Why'd we drop this one?  But it looks okay.

Originally the Xen sub-drivers uses the same virDriverPtr struct,
but now they have a dedicated  xenUnifiedDriver struct instead.

> > @@ -5018,8 +4917,18 @@ virDomainMigratePeer2Peer (virDomainPtr domain,
> >          return -1;
> >      }
> >  
> > -    if (!tempuri->server || STRPREFIX(tempuri->server, "localhost")) {
> > -        virLibConnError(VIR_ERR_INVALID_ARG, __FUNCTION__);
> > +    if (!tempuri->server) {
> > +        virReportInvalidArg(tempuri,
> > +                            _("server field in tempuri in %s must not be NULL"),
> > +                            __FUNCTION__);
> 
> Wrong message.  tempuri is something we constructed ourselves
> (virURIParse), and not something the user passed in.  Rather, we should
> say something like:
> 
> virReportInvalidArg(dconnuri,
>   _("Unable to parse server from dconnuri in %s"), __FUNCTION);

Ah, ok I missed that

> > @@ -8638,9 +8532,12 @@ virDomainPinVcpu(virDomainPtr domain, unsigned int vcpu,
> >          goto error;
> >      }
> >  
> > -    if ((vcpu > 32000) || (cpumap == NULL) || (maplen < 1)) {
> > -        virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__);
> > -       goto error;
> > +    virCheckNonNullArgGoto(cpumap, error);
> > +    virCheckPositiveArgGoto(maplen, error);
> > +
> > +    if ((unsigned short) vcpu != vcpu) {
> 
> Not strictly equivalent to '(vcpu > 32000)', but does a similar job (but
> only because 'vcpu' is unsigned; if it were signed, then you get into
> subtleties where it might not work).

I did this change, because several other places related to vCPUs were
already doing this cast+compare, so it is better to be consistent about
the precise point at which we raise errors.

> > @@ -11590,13 +11413,12 @@ virStoragePoolLookupByUUIDString(virConnectPtr conn,
> >          virDispatchError(NULL);
> >          return NULL;
> >      }
> > -    if (uuidstr == NULL) {
> > -        virLibConnError(VIR_ERR_INVALID_ARG, __FUNCTION__);
> > -        goto error;
> > -    }
> > +    virCheckNonNullArgGoto(uuidstr, error);
> >  
> >      if (virUUIDParse(uuidstr, uuid) < 0) {
> > -        virLibConnError(VIR_ERR_INVALID_ARG, __FUNCTION__);
> > +        virReportInvalidArg(uuidstr,
> > +                            _("uuidstr in %s must be a valid UUID"),
> > +                            __FUNCTION__);
> >          goto error;
> 
> The virUUIDParse() failure case is another frequent one that might be
> worth factoring into a one-liner.

Yes, definitely.

It is also my intent to simplify the checks for the read-only flag.

> > @@ -14381,15 +14143,15 @@ virSecretGetUUID(virSecretPtr secret, unsigned char *uuid)
> >          virDispatchError(NULL);
> >          return -1;
> >      }
> > -    if (uuid == NULL) {
> > -        virLibSecretError(VIR_ERR_INVALID_ARG, __FUNCTION__);
> > -        virDispatchError(secret->conn);
> > -        return -1;
> > -    }
> > +    virCheckNonNullArgGoto(uuid, error);
> >  
> >      memcpy(uuid, &secret->uuid[0], VIR_UUID_BUFLEN);
> >  
> >      return 0;
> > +
> > +error:
> > +    virDispatchError(NULL);
> 
> Oops.  Why the change from secret->conn to NULL?

Bugtastic.

> > @@ -16716,8 +16428,12 @@ virConnectDomainEventRegisterAny(virConnectPtr conn,
> >          virDispatchError(conn);
> >          return -1;
> >      }
> > -    if (eventID < 0 || eventID >= VIR_DOMAIN_EVENT_ID_LAST || cb == NULL) {
> > -        virLibConnError(VIR_ERR_INVALID_ARG, __FUNCTION__);
> > +    virCheckNonNullArgGoto(cb, error);
> > +    virCheckNonNegativeArgGoto(eventID, error);
> > +    if (eventID >= VIR_DOMAIN_EVENT_ID_LAST) {
> > +        virReportInvalidArg(eventID,
> > +                            _("eventID in %s must be less than %d"),
> > +                            __FUNCTION__, VIR_DOMAIN_EVENT_ID_LAST);
> 
> No change in functionality by this hunk (so if you fix it, make it a
> separate patch), but I think we have an independent bug here.  Suppose
> that I have a client compiled against 0.9.13 headers, but running
> against the 0.9.12 libvirt.so, and it is talking to an 0.9.13 server.
> This check prevents me from registering for an event new to 0.9.13, even
> though both client and server know about it, all because the
> intermediary RPC call is rejecting values.  I think the check for
> eventID too large has to be pushed down into the drivers, not here in
> libvirt.c.

Yes that is probably right.

> 
> > @@ -17131,20 +16843,23 @@ virDomainSnapshotCreateXML(virDomainPtr domain,
> >  
> >      if ((flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT) &&
> >          !(flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE)) {
> > -        virLibDomainError(VIR_ERR_INVALID_ARG,
> > -                          _("use of current flag requires redefine flag"));
> > +        virReportInvalidArg(flags,
> > +                            _("use of current flag in %s requires redefine flag"),
> 
> Elsewhere, you used quoting to make it obvious which flags were being
> talked about, as in: "use of 'current' flag in %s requires 'redefine' flag".

Good point.

> 
> 
> > @@ -18724,13 +18406,19 @@ int virDomainGetCPUStats(virDomainPtr domain,
> >       * ncpus must be non-zero unless params == NULL
> >       * nparams * ncpus must not overflow (RPC may restrict it even more)
> >       */
> > -    if (start_cpu < -1 ||
> > -        (start_cpu == -1 && ncpus != 1) ||
> > -        ((params == NULL) != (nparams == 0)) ||
> > -        (ncpus == 0 && params != NULL)) {
> > -        virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__);
> > +    if (start_cpu < -1 && ncpus != -1) {
> > +        virReportInvalidArg(start_cpu,
> > +                            _("start_cpu in %s must be -1 or greater if ncpus is not -1"),
> > +                            __FUNCTION__);
> 
> Huh?  You botched that conversion.  Consider:
> 
> start_cpu == -2, ncpus == 0 # old code rejected, new code lets through
> 
> start_cpu == -1, ncpus == -1 # old code rejected, new code lets through
> 
> I think you want:
> 
> if (start_cpu == -1) {
>   if (ncpus != 1) {
>     virReportInvalidArg(ncpus,
>       _("ncpus in %s must be 1 when start_cpu is -1"), __FUNCTION);
>     goto error;
>   }
> } else {
>   virCheckNonNegativeArgGoto(start_cpu, error);
> }


Hmm, don't know what I was thinking there !

> > +++ b/src/nodeinfo.c
> > @@ -451,8 +451,9 @@ int linuxNodeGetCPUStats(FILE *procstat,
> >      }
> >  
> >      if ((*nparams) != LINUX_NB_CPU_STATS) {
> > -        nodeReportError(VIR_ERR_INVALID_ARG,
> > -                        "%s", _("Invalid parameter count"));
> > +        virReportInvalidArg(*nparams,
> > +                            _("nparams in %s must be equal to %d"),
> > +                            __FUNCTION__, LINUX_NB_CPU_STATS);
> 
> Faithful conversion, but latent bug that we should fix.  I remember
> changing lots of similar situations to specifically allow *nparams to be
> smaller (truncate the result, good for new server talking to old library
> that only cares about the first parameter) or larger (ignore the extras,
> good for old server talking to new library that knows about more
> results, but can still behave when those results are not present).

Yes, it is a little odd, but before changing this, I'd have to examine
the code in more detail

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