[Libguestfs] [nbdkit PATCH] nbd: Fix sporadic use-after-free

Richard W.M. Jones rjones at redhat.com
Mon Dec 4 21:43:07 UTC 2017


On Mon, Dec 04, 2017 at 03:34:01PM -0600, Eric Blake wrote:
> Now that we properly clean up 'trans' in the reader thread, we
> must not dereference 'trans' from the write thread at any point
> after trans has been added to the list unless we have grabbed
> it back off the list ourselves, or we risk an occasional
> use-after-free or even double free that valgrind can detect.
> 
> Reported-by: Richard W.M. Jones <rjones at redhat.com>
> Fixes: cb189648f11df6c4f7287dcaed4bc856650e2c3b
> Signed-off-by: Eric Blake <eblake at redhat.com>
> ---
> 
> Perhaps I should also add more comments to the code about transfer of
> ownership of 'trans' between threads?
> 
>  plugins/nbd/nbd.c | 43 ++++++++++++++++++++++++++++++-------------
>  1 file changed, 30 insertions(+), 13 deletions(-)
> 
> diff --git a/plugins/nbd/nbd.c b/plugins/nbd/nbd.c
> index e79042c..9d40e87 100644
> --- a/plugins/nbd/nbd.c
> +++ b/plugins/nbd/nbd.c
> @@ -227,6 +227,26 @@ nbd_mark_dead (struct handle *h)
>    return -1;
>  }
> 
> +/* Find and remove the transaction corresponding to cookie from the list. */
> +static struct transaction *
> +find_trans_by_cookie (struct handle *h, uint64_t cookie)
> +{
> +  struct transaction **ptr;
> +  struct transaction *trans;
> +
> +  nbd_lock (h);
> +  ptr = &h->trans;
> +  while ((trans = *ptr) != NULL) {
> +    if (cookie == trans->u.cookie)
> +      break;
> +    ptr = &trans->next;
> +  }
> +  if (trans)
> +    *ptr = trans->next;
> +  nbd_unlock (h);
> +  return trans;
> +}
> +
>  /* Send a request, return 0 on success or -1 on write failure. */
>  static int
>  nbd_request_raw (struct handle *h, uint32_t type, uint64_t offset,
> @@ -260,6 +280,8 @@ nbd_request_full (struct handle *h, uint32_t type, uint64_t offset,
>  {
>    int err;
>    struct transaction *trans;
> +  int fd;
> +  uint64_t cookie;
> 
>    trans = calloc (1, sizeof *trans);
>    if (!trans) {
> @@ -282,9 +304,14 @@ nbd_request_full (struct handle *h, uint32_t type, uint64_t offset,
>    }
>    trans->next = h->trans;
>    h->trans = trans;
> +  fd = trans->u.fds[0];
> +  cookie = trans->u.cookie;
>    nbd_unlock (h);
> -  if (nbd_request_raw (h, type, offset, count, trans->u.cookie, req_buf) == 0)
> -    return trans->u.fds[0];
> +  if (nbd_request_raw (h, type, offset, count, cookie, req_buf) == 0)
> +    return fd;
> +  trans = find_trans_by_cookie (h, cookie);
> +  if (!trans)
> +    return nbd_mark_dead (h);
> 
>   err:
>    err = errno;
> @@ -309,7 +336,6 @@ static int
>  nbd_reply_raw (struct handle *h, int *fd)
>  {
>    struct reply rep;
> -  struct transaction **ptr;
>    struct transaction *trans;
>    void *buf;
>    uint32_t count;
> @@ -320,16 +346,7 @@ nbd_reply_raw (struct handle *h, int *fd)
>    if (be32toh (rep.magic) != NBD_REPLY_MAGIC)
>      return nbd_mark_dead (h);
>    nbdkit_debug ("received reply for cookie %#" PRIx64, rep.handle);
> -  nbd_lock (h);
> -  ptr = &h->trans;
> -  while ((trans = *ptr) != NULL) {
> -    if (rep.handle == trans->u.cookie)
> -      break;
> -    ptr = &trans->next;
> -  }
> -  if (trans)
> -    *ptr = trans->next;
> -  nbd_unlock (h);
> +  trans = find_trans_by_cookie (h, rep.handle);
>    if (!trans) {
>      nbdkit_error ("reply with unexpected cookie %#" PRIx64, rep.handle);
>      return nbd_mark_dead (h);
> -- 
> 2.14.3

I wasn't able to conclusively say that this fixes the problem
because it was quite rare and now isn't reproducing for me
at all.

Nevertheless, ACK.  Please push, thanks.

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-df lists disk usage of guests without needing to install any
software inside the virtual machine.  Supports Linux and Windows.
http://people.redhat.com/~rjones/virt-df/




More information about the Libguestfs mailing list