[Libguestfs] [libnbd PATCH v3 2/5] python: Alter lock for persistent buffer
Richard W.M. Jones
rjones at redhat.com
Thu Jun 9 14:27:36 UTC 2022
On Thu, Jun 09, 2022 at 08:34:44AM -0500, Eric Blake wrote:
> As long as we were wrapping a C buffer and not leaking any Python
> objects, we had to lock the nbd.Buffer object, to ensure that the
> PyCapsule was not released prematurely, as there was nothing else to
> hold on to. But now that the previous commit (4f15d0e9) swapped to
> wrapping a Python bytearray object, we are better off locking the
> PyMemoryView created from that object, for two reasons. First, if we
> are going to allow aio_{pread,pwrite} to take an arbitrary buffer-like
> object instead of requiring an nbd.Buffer object, then that's all we
> have available to lock. Second, hanging on to a PyMemoryView provides
> some nice guarantees that the underlying python buffer-like object
> won't be reallocated behind our backs. The following snippets from
> nbdsh use bad style (you should never access a ._* member outside of
> its class), but demonstrate the same issue we would be faced with once
> nbd.Buffer starts sharing rather than copying in the public
> .{to,from}_bytearray:
>
> Pre-patch:
> nbd> b = nbd.Buffer(10)
> nbd> c = h.aio_pread(b, 0)
> nbd> b._o.pop()
> 0
> nbd> b.size()
> 9
> nbd> h.poll(-1)
>
> Oops - we changed the size of the underlying bytearray prior to
> receiving the server's reply. If that caused python to reallocate the
> buffer underlying the bytearray, we've just clobbered random memory.
>
> Post-patch:
> nbd> b = nbd.Buffer(10)
> nbd> c = h.aio_pread(b, 0)
> nbd> b._o.pop()
> Traceback (most recent call last):
> File "/usr/lib64/python3.10/code.py", line 90, in runcode
> exec(code, self.locals)
> File "<console>", line 1, in <module>
> BufferError: Existing exports of data: object cannot be re-sized
Wow what a horrible error message! However it comes from Python, not
us, so there's not a lot we can do about it.
> nbd> h.poll(-1)
> nbd> h.aio_command_completed(c)
> True
> nbd> b._o.pop()
> 0
> nbd> b.size()
> 9
>
> There - now we are sure that as long as our read is pending, the
> underlying bytearray cannot be reallocated; but as soon as it
> completes, we have regained full use of the bytearray.
> ---
> generator/Python.ml | 15 +++++----------
> 1 file changed, 5 insertions(+), 10 deletions(-)
>
> diff --git a/generator/Python.ml b/generator/Python.ml
> index 6980034..20d7e78 100644
> --- a/generator/Python.ml
> +++ b/generator/Python.ml
> @@ -424,16 +424,12 @@ let
> pr " %s_view = nbd_internal_py_get_aio_view (%s, true);\n" n n;
> pr " if (!%s_view) goto out;\n" n;
> pr " py_%s = PyMemoryView_GET_BUFFER (%s_view);\n" n n;
> - pr " /* Increment refcount since buffer may be saved by libnbd. */\n";
> - pr " Py_INCREF (%s);\n" n;
> - pr " completion_user_data->buf = %s;\n" n
> + pr " completion_user_data->view = %s_view;\n" n
> | BytesPersistOut (n, _) ->
> pr " %s_view = nbd_internal_py_get_aio_view (%s, false);\n" n n;
> pr " if (!%s_view) goto out;\n" n;
> pr " py_%s = PyMemoryView_GET_BUFFER (%s_view);\n" n n;
> - pr " /* Increment refcount since buffer may be saved by libnbd. */\n";
> - pr " Py_INCREF (%s);\n" n;
> - pr " completion_user_data->buf = %s;\n" n
> + pr " completion_user_data->view = %s_view;\n" n
> | Closure { cbname } ->
> pr " %s.user_data = %s_user_data = alloc_user_data ();\n" cbname cbname;
> pr " if (%s_user_data == NULL) goto out;\n" cbname;
> @@ -538,8 +534,7 @@ let
> pr " if (%s.obj)\n" n;
> pr " PyBuffer_Release (&%s);\n" n
> | BytesOut (n, _) -> pr " Py_XDECREF (%s);\n" n
> - | BytesPersistIn (n, _) | BytesPersistOut (n, _) ->
> - pr " Py_XDECREF (%s_view);\n" n
> + | BytesPersistIn (n, _) | BytesPersistOut (n, _) -> ()
> | Closure { cbname } ->
> pr " free_user_data (%s_user_data);\n" cbname
> | Enum _ -> ()
> @@ -588,7 +583,7 @@ let
> pr " */\n";
> pr "struct user_data {\n";
> pr " PyObject *fn; /* Optional pointer to Python function. */\n";
> - pr " PyObject *buf; /* Optional pointer to persistent buffer. */\n";
> + pr " PyObject *view; /* Optional PyMemoryView of persistent buffer. */\n";
> pr "};\n";
> pr "\n";
> pr "static struct user_data *\n";
> @@ -609,7 +604,7 @@ let
> pr "\n";
> pr " if (data) {\n";
> pr " Py_XDECREF (data->fn);\n";
> - pr " Py_XDECREF (data->buf);\n";
> + pr " Py_XDECREF (data->view);\n";
> pr " free (data);\n";
> pr " }\n";
> pr "}\n";
I gather it works, but I don't understand why. What increments the
ref count on data->view?
Rich.
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
Fedora Windows cross-compiler. Compile Windows programs, test, and
build Windows installers. Over 100 libraries supported.
http://fedoraproject.org/wiki/MinGW
More information about the Libguestfs
mailing list