[Libguestfs] [libnbd PATCH] python: Accept buffers in nbd.Buffer.from_bytearray()

Richard W.M. Jones rjones at redhat.com
Thu May 26 08:16:18 UTC 2022


On Wed, May 25, 2022 at 08:27:15PM -0500, Eric Blake wrote:
> Prior to this patch, the following fails, but at least seems to give a
> sensible error:
> 
> $ nbdsh -c 'nbd.Buffer.from_bytearray(b"1"*8)'
> Traceback (most recent call last):
>   File "/usr/lib64/python3.10/runpy.py", line 196, in _run_module_as_main
>     return _run_code(code, main_globals, None,
>   File "/usr/lib64/python3.10/runpy.py", line 86, in _run_code
>     exec(code, run_globals)
>   File "/usr/lib64/python3.10/site-packages/nbd.py", line 2726, in <module>
>     nbdsh.shell()
>   File "/usr/lib64/python3.10/site-packages/nbdsh.py", line 139, in shell
>     exec(c, d, d)
>   File "<string>", line 1, in <module>
>   File "/usr/lib64/python3.10/site-packages/nbd.py", line 132, in from_bytearray
>     o = libnbdmod.aio_buffer_from_bytearray(ba)
> RuntimeError: parameter is not a bytearray
> 
> while this version 1 byte longer flat out segfaults:
> 
> $ nbdsh -c 'nbd.Buffer.from_bytearray(b"1"*9)'
> 
> and this one is subtly different:
> 
> $ nbdsh -c 'nbd.Buffer.from_bytearray(h)'
> Traceback (most recent call last):
> ...
>   File "/usr/lib64/python3.10/site-packages/nbd.py", line 132, in from_bytearray
>     o = libnbdmod.aio_buffer_from_bytearray(ba)
> MemoryError
> 
> That's because PyByteArray_AsString() blindly assumes that its
> argument is a PyByteArray, and goes haywire when it is not, unless we
> got lucky that the incorrectly-typed object behaves similarly enough
> (which, for byte literals, is size-dependent).
> 
> But Python already has a handy way to convert any object that supports
> the buffer interface into a bytearray.  Using it, we can now support
> many more parameters; passing in b"1"*9 now correctly creates a 9-byte
> buffer rather than failing.  And the error message for a non-buffer
> also improves:
> 
> $ ./run nbdsh -c 'nbd.Buffer.from_bytearray(h)'
> Traceback (most recent call last):
> ...
>   File "/home/eblake/libnbd/python/nbd.py", line 132, in from_bytearray
>     o = libnbdmod.aio_buffer_from_bytearray(ba)
> TypeError: cannot convert 'NBD' object to bytearray
> 
> (A reliable TypeError is always better than an unexpected MemoryError
> or segfault).
> ---
>  python/handle.c | 16 ++++++++++++++--
>  1 file changed, 14 insertions(+), 2 deletions(-)
> 
> diff --git a/python/handle.c b/python/handle.c
> index 9c08dc1..9fe3f8e 100644
> --- a/python/handle.c
> +++ b/python/handle.c
> @@ -1,5 +1,5 @@
>  /* NBD client library in userspace
> - * Copyright (C) 2013-2020 Red Hat Inc.
> + * Copyright (C) 2013-2022 Red Hat Inc.
>   *
>   * This library is free software; you can redistribute it and/or
>   * modify it under the terms of the GNU Lesser General Public
> @@ -159,6 +159,7 @@ PyObject *
>  nbd_internal_py_aio_buffer_from_bytearray (PyObject *self, PyObject *args)
>  {
>    PyObject *obj;
> +  PyObject *arr = NULL;
>    Py_ssize_t len;
>    void *data;
>    struct py_aio_buffer *buf;
> @@ -169,9 +170,17 @@ nbd_internal_py_aio_buffer_from_bytearray (PyObject *self, PyObject *args)
>                           &obj))
>      return NULL;
> 
> +  if (! PyByteArray_Check (obj)) {
> +    arr = PyByteArray_FromObject (obj);
> +    if (arr == NULL)
> +      return NULL;
> +    obj = arr;
> +  }
>    data = PyByteArray_AsString (obj);
>    if (!data) {
> -    PyErr_SetString (PyExc_RuntimeError, "parameter is not a bytearray");
> +    PyErr_SetString (PyExc_RuntimeError,
> +                     "parameter is not a bytearray or buffer");
> +    Py_XDECREF (arr);
>      return NULL;
>    }
>    len = PyByteArray_Size (obj);
> @@ -179,6 +188,7 @@ nbd_internal_py_aio_buffer_from_bytearray (PyObject *self, PyObject *args)
>    buf = malloc (sizeof *buf);
>    if (buf == NULL) {
>      PyErr_NoMemory ();
> +    Py_XDECREF (arr);
>      return NULL;
>    }
> 
> @@ -187,9 +197,11 @@ nbd_internal_py_aio_buffer_from_bytearray (PyObject *self, PyObject *args)
>    if (buf->data == NULL) {
>      PyErr_NoMemory ();
>      free (buf);
> +    Py_XDECREF (arr);
>      return NULL;
>    }
>    memcpy (buf->data, data, len);
> +  Py_XDECREF (arr);
> 
>    ret = PyCapsule_New (buf, aio_buffer_name, free_aio_buffer);
>    if (ret == NULL) {

Reviewed-by: Richard W.M. Jones <rjones at redhat.com>

Thanks!

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-p2v converts physical machines to virtual machines.  Boot with a
live CD or over the network (PXE) and turn machines into KVM guests.
http://libguestfs.org/virt-v2v


More information about the Libguestfs mailing list