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) {
--
2.36.1