[Libguestfs] [PATCH nbdkit 4/4] Add floppy plugin.

Eric Blake eblake at redhat.com
Tue Oct 30 16:54:39 UTC 2018


On 10/30/18 11:42 AM, Richard W.M. Jones wrote:

> 
>>> +  errno = 0;
>>> +  while ((d = readdir (DIR)) != NULL) {
>>> +    if (strcmp (d->d_name, ".") == 0 ||
>>> +        strcmp (d->d_name, "..") == 0)
>>> +      continue;

strcmp() leaves errno alone (well, POSIX doesn't guarantee that, but no 
sane implementation of strcmp() would change errno during a string compare)

>>> +
>>> +    if (lstat (d->d_name, &statbuf) == -1) {
>>> +      nbdkit_error ("stat: %s/%s: %m", dir, d->d_name);
>>> +      goto error2;
>>> +    }

However, errno is undefined if lstat() succeeds. If it fails, you don't 
call readdir() again; but if it succeeds, you cannot rely on libc having 
left errno == 0.

>>> +
>>> +    /* Directory. */
>>> +    if (S_ISDIR (statbuf.st_mode)) {
>>> +      if (visit_subdirectory (dir, d->d_name, &statbuf, di, floppy) == -1)
>>> +        goto error2;
>>> +    }

And then you have to audit whether your visit_subdirectory() code left 
errno unchanged...

>>> +    /* Regular file. */
>>> +    else if (S_ISREG (statbuf.st_mode)) {
>>> +      if (visit_file (dir, d->d_name, &statbuf, di, floppy) == -1)
>>> +        goto error2;
>>> +    }

...and visit_file().  Looking at just visit_file():

> +static int
> +visit_file (const char *dir, const char *name,
> +            const struct stat *statbuf, size_t di,
> +            struct virtual_floppy *floppy)
> +{
> +  void *np;
> +  char *host_path;
> +  size_t fi, i;
> +
> +  if (asprintf (&host_path, "%s/%s", dir, name) == -1) {
> +    nbdkit_error ("asprintf: %m");
> +    return -1;
> +  }

asprintf() may have modified errno...

> +  /* Add to global list of files. */
> +  fi = floppy->nr_files;
> +  np = realloc (floppy->files, sizeof (struct file) * (fi+1));

Likewise a successful realloc()...

> +  if (np == NULL) {
> +    nbdkit_error ("realloc: %m");
> +    free (host_path);
> +    return -1;
> +  }
> +  floppy->files = np;
> +  floppy->nr_files++;
> +  floppy->files[fi].name = strdup (name);

even a successful strdup(). Etc.

In short, POSIX guarantees that errno is set on failure, but leaves 
errno undefined on success after many library functions (there are a 
few, like free(), where I have been working to get POSIX to explicitly 
state that errno is left unchanged - but those are mainly functions you 
use on cleanup paths, where it is easier to program if you can guarantee 
that your cleanup doesn't corrupt the original error).

>> Need to reset errno back to 0 before looping back to the next
>> readdir() call.
> 
> Hmm, really?  If so, this is a class of bugs we have made throughout
> libguestfs code.

Yes, this is a common bug, and it looks like libguestfs needs an audit 
fixing it.  The only SAFE way to call readdir() is to immediately set 
errno = 0 just beforehand, on every iteration (not just the first).

>  Why would errno be set in the loop?

Because you called into libc.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org




More information about the Libguestfs mailing list