Subject: Re: bug#46796: Cuirass & pointer finalization.

From: Ludovic Courtès <[email-masked]>
Date: Mon, 01 Mar 2021 10:37:33 +0100

Hi!

Mathieu Othacehe <[email-masked]> skribis:

> Here's a Valgrind backtrace:
>
> ==97844== Thread 17:
> ==97844== Invalid read of size 4
> ==97844==    at 0x114465B9: zmq::msg_t::close() (in /gnu/store/zd9lbfqa3170nsfd4177dnr38k1sjbnc-zeromq-4.3.4/lib/libzmq.so.5.2.4)

First, is this function idempotent?  (Is it OK to close an msg_t
multiple times.)

Second, remember that finalizers can run in a separate thread.  Thus,
you must make sure there are no other threads, such as ZMQ’s internal
threads, still operating on the object when it is freed.

> ==97844==    by 0x3A58E98F: ??? 
> ==97844==    by 0x489AC78: chained_finalizer (in /gnu/store/q8brh7j5mwy0hbrly6hjb1m3wwndxqc8-guile-3.0.5/lib/libguile-3.0.so.1.3.0)
> ==97844==    by 0x49A16EE: GC_invoke_finalizers (in /gnu/store/iycnpxxrg8m9wf9w58d6zvp9sdby6m9d-libgc-7.6.12/lib/libgc.so.1.3.6)
> ==97844==    by 0x489AF08: scm_run_finalizers (in /gnu/store/q8brh7j5mwy0hbrly6hjb1m3wwndxqc8-guile-3.0.5/lib/libguile-3.0.so.1.3.0)
> ==97844==    by 0x489AF8C: finalization_thread_proc (in /gnu/store/q8brh7j5mwy0hbrly6hjb1m3wwndxqc8-guile-3.0.5/lib/libguile-3.0.so.1.3.0)
> ==97844==    by 0x488BB09: c_body (in /gnu/store/q8brh7j5mwy0hbrly6hjb1m3wwndxqc8-guile-3.0.5/lib/libguile-3.0.so.1.3.0)
> ==97844==    by 0x4913148: vm_regular_engine (in /gnu/store/q8brh7j5mwy0hbrly6hjb1m3wwndxqc8-guile-3.0.5/lib/libguile-3.0.so.1.3.0)
> ==97844==    by 0x49145B4: scm_call_n (in /gnu/store/q8brh7j5mwy0hbrly6hjb1m3wwndxqc8-guile-3.0.5/lib/libguile-3.0.so.1.3.0)
> ==97844==    by 0x4890BB9: scm_call_2 (in /gnu/store/q8brh7j5mwy0hbrly6hjb1m3wwndxqc8-guile-3.0.5/lib/libguile-3.0.so.1.3.0)
> ==97844==    by 0x48923B9: scm_c_with_exception_handler (in /gnu/store/q8brh7j5mwy0hbrly6hjb1m3wwndxqc8-guile-3.0.5/lib/libguile-3.0.so.1.3.0)
> ==97844==    by 0x4909C3C: scm_c_catch (in /gnu/store/q8brh7j5mwy0hbrly6hjb1m3wwndxqc8-guile-3.0.5/lib/libguile-3.0.so.1.3.0)
> ==97844==  Address 0x7373313569316263 is not stack'd, malloc'd or (recently) free'd
>
>
> It looks like the finalizer is operating on a memory region that has
> already been free'd. The documentation associated with the
> finalization functions in <gc.h> says:
>
>         /* When obj is no longer accessible, invoke             */
>         /* (*fn)(obj, cd).  If a and b are inaccessible, and    */
>         /* a points to b (after disappearing links have been    */
>         /* made to disappear), then only a will be              */
>
>
> As far as I understand, OBJ is the wrapped pointer to the bytevector
> created in "zmq-msg-init". There's a weak reference between the pointer
> and the bytevector that is introduced by "register_weak_reference" in
> "bytevector->pointer".

There are (roughly) three objects here: the “msg”, the pointer object,
and the bytevector that pointer refers to.

The bytevector may be freed when the pointer object becomes unreachable.

But you probably also need a weak link from the “msg” object to the
pointer object to ensure that the pointer object outlives the msg
object.

You also need to check the zmq::msg_t::close memory semantics: does it
free the data associated with the message?  If so, that’s redundant with
the pointer finalizer.

> My interrogation is: do I have the guarantee that the pointer and its
> references are still readable from within the finalizer? The above
> snippet says that FN is invoked when OBJ is unaccessible, but does this
> mean its content may have already been collected?

Not sure, but most likely the problem is at a higher layer.  :-)

If you can get a reduced test case to run under ‘rr’, that should allow
you to see where the message was first freed.

This is all pretty vague and general, but I HTH!

Ludo’.