[Libguestfs] [nbdkit PATCH] server: Saner filter .close calls

Richard W.M. Jones rjones at redhat.com
Wed Sep 18 15:52:11 UTC 2019


On Wed, Sep 18, 2019 at 10:07:22AM -0500, Eric Blake wrote:
> I found a core dump:
> 
> term1$ nbdkit -fv --filter=xz null xz-max-depth=$((0x4000000000000000))
> term1...
> term2$ qemu-nbd --list
> term1...
> nbdkit: debug: null: open readonly=1
> nbdkit: error: calloc: Cannot allocate memory
> nbdkit: debug: xz: close
> Segmentation fault (core dumped)
> 
> (Note that the use of --run or daemonizing nbdkit makes it a bit
> harder to see the core dump, but it is still happening.)
> 
> This happens because xz's .open fails after null's has succeeded, so
> the cleanup code sees that it needs to call the .close chain (based
> solely on whether the plugin's handle is non-NULL).  However, our code
> would call into a filter's .close even if handle was NULL (this is
> because we did not distinguish between a filter that failed .open,
> like xz, vs. a filter that lacks an override for .open).  And calling
> xz.close(NULL) causes a SIGSEGV.  Of course, we could patch the xz
> filter to paper over this by short-circuiting if handle is NULL, but a
> more generic fix to this is to make filters.c always set a non-NULL
> handle on .open success, then only call .close when the handle was
> set, because that's the only time .open succeeded.
> 
> Conversely, if a plugin's .open fails, we skip .close for the entire
> backend chain.  This could be problematic (a memory leak) if any of
> our filters had returned success to .open without calling the
> backend's .open - but fortunately all of our filters called next()
> prior to doing anything locally.  Still, we can ensure that things are
> sane by asserting that if .open succeeded, the backend's handle is
> also set.
> 
> Signed-off-by: Eric Blake <eblake at redhat.com>
> ---
>  server/backend.c |  9 ++++++++-
>  server/filters.c | 12 ++++++++----
>  2 files changed, 16 insertions(+), 5 deletions(-)
> 
> diff --git a/server/backend.c b/server/backend.c
> index a637f754..b918adff 100644
> --- a/server/backend.c
> +++ b/server/backend.c
> @@ -172,6 +172,7 @@ int
>  backend_open (struct backend *b, struct connection *conn, int readonly)
>  {
>    struct b_conn_handle *h = &conn->handles[b->i];
> +  int r;
> 
>    debug ("%s: open readonly=%d", b->name, readonly);
> 
> @@ -179,7 +180,13 @@ backend_open (struct backend *b, struct connection *conn, int readonly)
>    assert (h->can_write == -1);
>    if (readonly)
>      h->can_write = 0;
> -  return b->open (b, conn, readonly);
> +  r = b->open (b, conn, readonly);
> +  if (r == 0) {
> +    assert (h->handle != NULL);
> +    if (b->i)
> +      assert (conn->handles[b->i - 1].handle);
> +  }
> +  return r;
>  }
> 
>  int
> diff --git a/server/filters.c b/server/filters.c
> index 5bdc8aa7..1ee62829 100644
> --- a/server/filters.c
> +++ b/server/filters.c
> @@ -210,10 +210,13 @@ filter_open (struct backend *b, struct connection *conn, int readonly)
>      if (handle == NULL)
>        return -1;
>      backend_set_handle (b, conn, handle);
> -    return 0;
>    }
> -  else
> -    return backend_open (b->next, conn, readonly);
> +  else {
> +    if (backend_open (b->next, conn, readonly) == -1)
> +      return -1;
> +    backend_set_handle (b, conn, NBDKIT_HANDLE_NOT_NEEDED);
> +  }
> +  return 0;
>  }
> 
>  static void
> @@ -224,8 +227,9 @@ filter_close (struct backend *b, struct connection *conn)
> 
>    debug ("%s: close", b->name);
> 
> -  if (f->filter.close)
> +  if (handle && f->filter.close)
>      f->filter.close (handle);
> +  backend_set_handle (b, conn, NULL);
>    b->next->close (b->next, conn);
>  }

Looks good.  Also I ran the tests & valgrind, and I ran
the reproducer on the updated nbdkit which was fine too, so:

ACK

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-builder quickly builds VMs from scratch
http://libguestfs.org/virt-builder.1.html




More information about the Libguestfs mailing list