[PD] [devel] more bindlist-related crashes (was: self-destruction)

Krzysztof Czaja czaja at chopin.edu.pl
Thu Dec 27 13:11:02 CET 2001


hi,

in my previous longish post I reported that sending self-destruction
messages to a canvas (`menuclose' or `cut') may prove deadly.  This
`misfeature', if taken on its own, is not important -- after all,
hacking has a price, ``one gets what one asked for'', etc.

But there is another weak spot -- receive symbol of gui objects.
This symbol may be changed via `receive' message, and in certain ways
such a change may easily crash Pd:  during bindlist traversal any
bindlist element may send a message causing unfortunate modification
of its bindlist, i.e. leaving a fatal dangling pointer.

To prove the point, here is an attached test patch `gui-crashes.pd',
which crashes Pd in self-unbinding cases `abc1' and `ab1', and also
in `ab2' case (bindlist annihilation).  After applying lookahead
safeguard from my previous post, Pd will crash in cases `abc2' and
`ab2' (next element unbinding).

While self-destruction related crashes may be avoided easily, there
are no simple safeguards against arbitrary un/rebinding of objects.

One quite elaborate (and costly) solution is to use some kind of
a locking strategy.  An example of how to lock current bindlist
element is attached in `m_pd.c.diff'.

Another standard solution is to use `smart' pointers.

Yet another solution is to get rid of (or at least to warn against)
any message being a potential bindlist modifier.

Krzysztof

Btw. ``if (glist_isvisible())'' tests are missing before redraw
commands in iemgui_send/receive() of 0.35test4.
-------------- next part --------------
#N canvas 240 51 439 297 12;
#X msg 64 35 abc1;
#X msg 190 35 abc2;
#X msg 316 35 abc3;
#X msg 133 165 ab1;
#X msg 267 165 ab2;
#N canvas 0 0 523 344 abc1 0;
#X obj 226 21 inlet;
#X obj 226 62 symbol;
#X msg 226 102 receive empty \, receive \$1 \; \$1 bang;
#X obj 226 157 t a a a a;
#X obj 26 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1
-1;
#X obj 156 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1
;
#X obj 276 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1
-1;
#X obj 26 266 print one;
#X obj 156 295 print two;
#X obj 276 266 print three;
#X msg 81 102 receive empty;
#X obj 402 225 print abc1;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X connect 2 0 3 0;
#X connect 3 0 4 0;
#X connect 3 1 5 0;
#X connect 3 2 6 0;
#X connect 3 3 11 0;
#X connect 4 0 10 0;
#X connect 4 0 7 0;
#X connect 5 0 8 0;
#X connect 6 0 9 0;
#X connect 10 0 4 0;
#X restore 64 90 pd abc1;
#N canvas 0 0 523 344 abc2 0;
#X obj 226 21 inlet;
#X obj 226 62 symbol;
#X msg 226 102 receive empty \, receive \$1 \; \$1 bang;
#X obj 226 157 t a a a a;
#X obj 26 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1
-1;
#X obj 156 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1
;
#X obj 276 216 bng 30 250 50 0 empty epmty empty 20 8 64 8 -262144 -1
-1;
#X obj 26 266 print one;
#X obj 156 295 print two;
#X obj 276 266 print three;
#X obj 402 225 print abc2;
#X msg 81 102 receive empty;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X connect 2 0 3 0;
#X connect 3 0 4 0;
#X connect 3 1 5 0;
#X connect 3 2 6 0;
#X connect 3 3 10 0;
#X connect 4 0 11 0;
#X connect 4 0 7 0;
#X connect 5 0 8 0;
#X connect 6 0 9 0;
#X connect 11 0 5 0;
#X restore 190 90 pd abc2;
#N canvas 0 0 523 344 abc3 0;
#X obj 226 21 inlet;
#X obj 226 62 symbol;
#X msg 226 102 receive empty \, receive \$1 \; \$1 bang;
#X obj 226 157 t a a a a;
#X obj 26 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1
-1;
#X obj 156 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1
;
#X obj 276 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1
-1;
#X obj 26 266 print one;
#X obj 156 295 print two;
#X obj 276 266 print three;
#X msg 81 102 receive empty;
#X obj 402 225 print abc3;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X connect 2 0 3 0;
#X connect 3 0 4 0;
#X connect 3 1 5 0;
#X connect 3 2 6 0;
#X connect 3 3 11 0;
#X connect 4 0 10 0;
#X connect 4 0 7 0;
#X connect 5 0 8 0;
#X connect 6 0 9 0;
#X connect 10 0 6 0;
#X restore 316 90 pd abc3;
#N canvas 0 0 523 344 ab1 0;
#X obj 226 21 inlet;
#X obj 226 62 symbol;
#X msg 226 102 receive empty \, receive \$1 \; \$1 bang;
#X obj 26 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1
-1;
#X obj 156 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1
;
#X obj 26 266 print one;
#X obj 156 295 print two;
#X msg 81 102 receive empty;
#X obj 298 216 print ab1;
#X obj 226 157 t a a a;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X connect 2 0 9 0;
#X connect 3 0 7 0;
#X connect 3 0 5 0;
#X connect 4 0 6 0;
#X connect 7 0 3 0;
#X connect 9 0 3 0;
#X connect 9 1 4 0;
#X connect 9 2 8 0;
#X restore 133 221 pd ab1;
#N canvas 0 0 523 344 ab2 0;
#X obj 226 21 inlet;
#X obj 226 62 symbol;
#X msg 226 102 receive empty \, receive \$1 \; \$1 bang;
#X obj 26 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1
-1;
#X obj 156 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1
;
#X obj 26 266 print one;
#X obj 156 295 print two;
#X msg 81 102 receive empty;
#X obj 226 157 t a a a;
#X obj 298 216 print ab2;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X connect 2 0 8 0;
#X connect 3 0 7 0;
#X connect 3 0 5 0;
#X connect 4 0 6 0;
#X connect 7 0 4 0;
#X connect 8 0 3 0;
#X connect 8 1 4 0;
#X connect 8 2 9 0;
#X restore 267 221 pd ab2;
#X connect 0 0 5 0;
#X connect 1 0 6 0;
#X connect 2 0 7 0;
#X connect 3 0 8 0;
#X connect 4 0 9 0;
-------------- next part --------------
--- m_pd-orig.c	Fri Dec 21 11:04:52 2001
+++ m_pd.c	Thu Dec 27 12:57:57 2001
@@ -46,83 +46,121 @@
 {
     t_pd *e_who;
     struct _bindelem *e_next;
+    int e_locked:1;
+    int e_freed:1;
 } t_bindelem;
 
 typedef struct _bindlist
 {
     t_pd b_pd;
     t_bindelem *b_list;
+    t_symbol *b_symbol;
 } t_bindlist;
 
+#define bindelem_lock(x)  ((x)->e_locked = 1)
+
+/* Return 1 if a not visited object is being left after bindlist annihilation,
+   otherwise return 0.  First argument upon return holds a pointer to next
+   element in a possibly modified bindlist (or null).
+*/
+static int bindelem_unlock(t_bindelem **x, t_symbol *s)
+{
+            /* symbol has no bindings, iff we were unbound as the last object */
+    if (s->s_thing
+            /* bindlist is gone, iff we hold the last remaining object */
+	&& *s->s_thing == bindlist_class)
+    {
+	    /* there is at least one other object bound */
+	t_bindelem *x1 = (*x)->e_next;
+	(*x)->e_locked = 0;
+	if ((*x)->e_freed)
+	{
+	    pd_unbind((*x)->e_who, s);
+	    if (*s->s_thing == bindlist_class) *x = x1;
+	    else if (x1)
+	    {
+		*x = 0;
+		return (1);  /* the only remaining object was not visited yet */
+	    }
+	}
+	else *x = x1;
+    }
+    else *x = 0;
+    return (0);
+}
+
 static void bindlist_bang(t_bindlist *x)
 {
     t_bindelem *e = x->b_list;
-#if CRASHY /* the loop we have now */
-    for (e = x->b_list; e; e = e->e_next)
-    	pd_bang(e->e_who);
-#else /* safer loop */
+    t_symbol *t = x->b_symbol;
     while (e)
     {
-	t_pd *who = e->e_who;
-	e = e->e_next;
-    	pd_bang(who);
+	bindelem_lock(e);
+    	pd_bang(e->e_who);
+	if (bindelem_unlock(&e, t)) pd_bang(t->s_thing);
     }
-#endif
 }
 
 static void bindlist_float(t_bindlist *x, t_float f)
 {
     t_bindelem *e = x->b_list;
-#if 1 /* the loop we have now */
-    for (e = x->b_list; e; e = e->e_next)
-    	pd_float(e->e_who, f);
-#else /* safer loop */
+    t_symbol *t = x->b_symbol;
     while (e)
     {
-	t_pd *who = e->e_who;
-	e = e->e_next;
-    	pd_float(who, f);
+	bindelem_lock(e);
+    	pd_float(e->e_who, f);
+	if (bindelem_unlock(&e, t)) pd_float(t->s_thing, f);
     }
-#endif
 }
 
 static void bindlist_symbol(t_bindlist *x, t_symbol *s)
 {
-    t_bindelem *e;
-    for (e = x->b_list; e; e = e->e_next)
+    t_bindelem *e = x->b_list;
+    t_symbol *t = x->b_symbol;
+    while (e)
+    {
+	bindelem_lock(e);
     	pd_symbol(e->e_who, s);
+	if (bindelem_unlock(&e, t)) pd_symbol(t->s_thing, s);
+    }
 }
 
 static void bindlist_pointer(t_bindlist *x, t_gpointer *gp)
 {
-    t_bindelem *e;
-    for (e = x->b_list; e; e = e->e_next)
+    t_bindelem *e = x->b_list;
+    t_symbol *t = x->b_symbol;
+    while (e)
+    {
+	bindelem_lock(e);
     	pd_pointer(e->e_who, gp);
+	if (bindelem_unlock(&e, t)) pd_pointer(t->s_thing, gp);
+    }
 }
 
 static void bindlist_list(t_bindlist *x, t_symbol *s,
     int argc, t_atom *argv)
 {
-    t_bindelem *e;
-    for (e = x->b_list; e; e = e->e_next)
+    t_bindelem *e = x->b_list;
+    t_symbol *t = x->b_symbol;
+    while (e)
+    {
+	bindelem_lock(e);
     	pd_list(e->e_who, s, argc, argv);
+	if (bindelem_unlock(&e, t)) pd_list(t->s_thing, s, argc, argv);
+    }
 }
 
 static void bindlist_anything(t_bindlist *x, t_symbol *s,
     int argc, t_atom *argv)
 {
     t_bindelem *e = x->b_list;
-#if CRASHY /* the loop we have now */
-    for (e = x->b_list; e; e = e->e_next)
-    	pd_typedmess(e->e_who, s, argc, argv);
-#else /* safer loop */
+    t_symbol *t = x->b_symbol;
     while (e)
     {
-	t_pd *who = e->e_who;
-	e = e->e_next;
-    	pd_typedmess(who, s, argc, argv);
+	bindelem_lock(e);
+    	pd_typedmess(e->e_who, s, argc, argv);
+	if (bindelem_unlock(&e, t)) pd_typedmess(t->s_thing, s, argc, argv);
     }
-#endif
 }
 
 void m_pd_setup(void)
@@ -147,6 +185,7 @@
     	    t_bindelem *e = (t_bindelem *)getbytes(sizeof(t_bindelem));
     	    e->e_next = b->b_list;
     	    e->e_who = x;
+	    e->e_locked = e->e_freed = 0;
     	    b->b_list = e;
     	}
     	else
@@ -155,10 +194,13 @@
     	    t_bindelem *e1 = (t_bindelem *)getbytes(sizeof(t_bindelem));
     	    t_bindelem *e2 = (t_bindelem *)getbytes(sizeof(t_bindelem));
     	    b->b_list = e1;
+	    b->b_symbol = s;
     	    e1->e_who = x;
     	    e1->e_next = e2;
+	    e1->e_locked = e1->e_freed = 0;
     	    e2->e_who = s->s_thing;
     	    e2->e_next = 0;
+	    e2->e_locked = e2->e_freed = 0;
     	    s->s_thing = &b->b_pd;
     	}
     }
@@ -178,14 +220,22 @@
     	t_bindelem *e, *e2;
     	if ((e = b->b_list)->e_who == x)
     	{
-    	    b->b_list = e->e_next;
-    	    freebytes(e, sizeof(t_bindelem));
+	    if (e->e_locked) e->e_freed = 1;
+	    else
+	    {
+		b->b_list = e->e_next;
+		freebytes(e, sizeof(t_bindelem));
+	    }
     	}
     	else for (e = b->b_list; e2 = e->e_next; e = e2)
     	    if (e2->e_who == x)
     	{
-    	    e->e_next = e2->e_next;
-    	    freebytes(e2, sizeof(t_bindelem));
+	    if (e2->e_locked) e2->e_freed = 1;
+	    else
+	    {
+		e->e_next = e2->e_next;
+		freebytes(e2, sizeof(t_bindelem));
+	    }
     	    break;
     	}
     	if (!b->b_list->e_next)


More information about the Pd-list mailing list