[Ecls-list] ECL 10.3.1 GC finalization bug and patch
Ram Krishnan
kriyative at gmail.com
Sun Mar 14 01:30:04 UTC 2010
Hi,
There's a fairly serious bug in the GC finalization code in
10.3.1 (and 9.12.3). The effect of the bug is that if a program
allocates multiple ffi objects, and calls SET-FINALIZER on them, then
under some circumstances the finalizer may be called with a completely
bogus pointer object, or just crash with a SIGSEGV. The issue is in
the queueing_finalizer() function in src/c/alloc_2.d.
The line:
GC_register_finalizer_no_order(aux,
(GC_finalization_proc*)group_finalizer, NULL, &ofn, &odata);
should really be:
GC_register_finalizer_no_order(l,
(GC_finalization_proc*)group_finalizer, NULL, &ofn, &odata);
i.e., the first arg should be the head of the queue (l) not the tail.
Otherwise, the queued finalization records could end up being total
trash. See attached patch (gc_finalization-patch.txt) for a fix to the
corruption issue.
However, while this fixes the corruption, the user finalization code
is no longer invoked. I believe the effect of queueing_finalizer() is
to that the `queue' (list of finalization records) remains live, and
isn't gc'ed.
After staring at this function for a while I gave up on what the
motivation behind it is. All that queueing_finalizer() does is to
create a list of the objects and their finalizers, which were passed
to it, so that those finalizers may be called at some later point. I
can't figure out what the benefit of deferring these finalization
calls is. Anyway, the simplest fix is to not do any queueing, and just
invoke the finalizer, like so:
static void
basic_finalizer(cl_object o, cl_object finalizer)
{
if (finalizer != Cnil && finalizer != NULL) {
if (finalizer == Ct) {
CL_NEWENV_BEGIN {
standard_finalizer(o);
} CL_NEWENV_END;
} else {
CL_NEWENV_BEGIN {
cl_funcall(2, finalizer, o);
} CL_NEWENV_END;
}
}
}
And, change ecl_set_finalizer_unprotected() and si_get_finalizer() to
use this basic_finalizer() instead. I've attached another patch
(gc_finalization-patch-2.txt) which includes this and the first fix as
well.
If there's a reason for the current behavior of queueing_finalizer(),
I look forward to understanding it.
Cheers,
-ram
-------------- next part --------------
Index: src/c/alloc_2.d
===================================================================
RCS file: /cvsroot/ecls/ecl/src/c/alloc_2.d,v
retrieving revision 1.136
diff -u -r1.136 alloc_2.d
--- src/c/alloc_2.d 28 Feb 2010 14:21:21 -0000 1.136
+++ src/c/alloc_2.d 14 Mar 2010 00:36:03 -0000
@@ -1097,7 +1097,7 @@
void *odata;
cl_core.to_be_finalized = aux;
ecl_disable_interrupts_env(the_env);
- GC_register_finalizer_no_order(aux, (GC_finalization_proc*)group_finalizer, NULL, &ofn, &odata);
+ GC_register_finalizer_no_order(l, (GC_finalization_proc*)group_finalizer, NULL, &ofn, &odata);
ecl_enable_interrupts_env(the_env);
} else {
ECL_RPLACD(l, aux);
@@ -1106,6 +1106,22 @@
}
}
+static void
+basic_finalizer(cl_object o, cl_object finalizer)
+{
+ if (finalizer != Cnil && finalizer != NULL) {
+ if (finalizer == Ct) {
+ CL_NEWENV_BEGIN {
+ standard_finalizer(o);
+ } CL_NEWENV_END;
+ } else {
+ CL_NEWENV_BEGIN {
+ cl_funcall(2, finalizer, o);
+ } CL_NEWENV_END;
+ }
+ }
+}
+
cl_object
si_get_finalizer(cl_object o)
{
@@ -1117,7 +1133,7 @@
GC_register_finalizer_no_order(o, (GC_finalization_proc)0, 0, &ofn, &odata);
if (ofn == 0) {
output = Cnil;
- } else if (ofn == (GC_finalization_proc)queueing_finalizer) {
+ } else if (ofn == (GC_finalization_proc)basic_finalizer) {
output = (cl_object)odata;
} else {
output = Cnil;
@@ -1137,7 +1153,7 @@
0, &ofn, &odata);
} else {
GC_finalization_proc newfn;
- newfn = (GC_finalization_proc)queueing_finalizer;
+ newfn = (GC_finalization_proc)basic_finalizer;
GC_register_finalizer_no_order(o, newfn, finalizer,
&ofn, &odata);
}
-------------- next part --------------
Index: src/c/alloc_2.d
===================================================================
RCS file: /cvsroot/ecls/ecl/src/c/alloc_2.d,v
retrieving revision 1.136
diff -u -r1.136 alloc_2.d
--- src/c/alloc_2.d 28 Feb 2010 14:21:21 -0000 1.136
+++ src/c/alloc_2.d 14 Mar 2010 00:31:32 -0000
@@ -1097,7 +1097,7 @@
void *odata;
cl_core.to_be_finalized = aux;
ecl_disable_interrupts_env(the_env);
- GC_register_finalizer_no_order(aux, (GC_finalization_proc*)group_finalizer, NULL, &ofn, &odata);
+ GC_register_finalizer_no_order(l, (GC_finalization_proc*)group_finalizer, NULL, &ofn, &odata);
ecl_enable_interrupts_env(the_env);
} else {
ECL_RPLACD(l, aux);
More information about the ecl-devel
mailing list