<p>Neels Hofmeyr has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/13391">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">add fsm_dealloc_test.c<br><br>Despite efforts to properly handle "GONE" events and entering a ST_DESTROYING<br>only once, so far this test runs straight into a heap use-after-free. With<br>current fsm.c, it is hard to resolve the situation with the objects named<br>"other" also causing deallocations besides the FSM instance parent/child<br>relations.<br><br>For illustration, add an "expected" test output file fsm_dealloc_test.err,<br>making this pass will follow in a subsequent patch.<br><br>Change-Id: If801907c541bca9f524c9e5fd22ac280ca16979a<br>---<br>M tests/Makefile.am<br>A tests/fsm/fsm_dealloc_test.c<br>A tests/fsm/fsm_dealloc_test.err<br>3 files changed, 606 insertions(+), 1 deletion(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/91/13391/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/tests/Makefile.am b/tests/Makefile.am</span><br><span>index 04393f5..afc95a3 100644</span><br><span>--- a/tests/Makefile.am</span><br><span>+++ b/tests/Makefile.am</span><br><span>@@ -50,7 +50,11 @@</span><br><span> endif</span><br><span> </span><br><span> if ENABLE_CTRL</span><br><span style="color: hsl(0, 100%, 40%);">-check_PROGRAMS += ctrl/ctrl_test fsm/fsm_test</span><br><span style="color: hsl(120, 100%, 40%);">+check_PROGRAMS += \</span><br><span style="color: hsl(120, 100%, 40%);">+    ctrl/ctrl_test \</span><br><span style="color: hsl(120, 100%, 40%);">+      fsm/fsm_test \</span><br><span style="color: hsl(120, 100%, 40%);">+        fsm/fsm_dealloc_test \</span><br><span style="color: hsl(120, 100%, 40%);">+        $(NULL)</span><br><span> endif</span><br><span> </span><br><span> if ENABLE_STATS_TEST</span><br><span>@@ -206,6 +210,9 @@</span><br><span>   $(top_builddir)/src/gsm/libosmogsm.la \</span><br><span>      $(top_builddir)/src/vty/libosmovty.la</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+fsm_fsm_dealloc_test_SOURCES = fsm/fsm_dealloc_test.c</span><br><span style="color: hsl(120, 100%, 40%);">+fsm_fsm_dealloc_test_LDADD = $(LDADD)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> write_queue_wqueue_test_SOURCES = write_queue/wqueue_test.c</span><br><span> </span><br><span> socket_socket_test_SOURCES = socket/socket_test.c</span><br><span>diff --git a/tests/fsm/fsm_dealloc_test.c b/tests/fsm/fsm_dealloc_test.c</span><br><span>new file mode 100644</span><br><span>index 0000000..5a493ad</span><br><span>--- /dev/null</span><br><span>+++ b/tests/fsm/fsm_dealloc_test.c</span><br><span>@@ -0,0 +1,476 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Scenarios of parent/child FSM instances cleaning up and deallocating from various triggers. */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <talloc.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/application.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/use_count.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum event {</span><br><span style="color: hsl(120, 100%, 40%);">+ EV_DESTROY,</span><br><span style="color: hsl(120, 100%, 40%);">+   EV_CHILD_GONE,</span><br><span style="color: hsl(120, 100%, 40%);">+        EV_OTHER_GONE,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct value_string test_fsm_event_names[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_VALUE_STRING(EV_DESTROY),</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_VALUE_STRING(EV_CHILD_GONE),</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_VALUE_STRING(EV_OTHER_GONE),</span><br><span style="color: hsl(120, 100%, 40%);">+     {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum state {</span><br><span style="color: hsl(120, 100%, 40%);">+        ST_ALIVE,</span><br><span style="color: hsl(120, 100%, 40%);">+     ST_DESTROYING,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum objname {</span><br><span style="color: hsl(120, 100%, 40%);">+  root = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+      branch0,</span><br><span style="color: hsl(120, 100%, 40%);">+       twig0a,</span><br><span style="color: hsl(120, 100%, 40%);">+       twig0b,</span><br><span style="color: hsl(120, 100%, 40%);">+      branch1,</span><br><span style="color: hsl(120, 100%, 40%);">+       twig1a,</span><br><span style="color: hsl(120, 100%, 40%);">+       twig1b,</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   other,</span><br><span style="color: hsl(120, 100%, 40%);">+        scene_size</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct scene {</span><br><span style="color: hsl(120, 100%, 40%);">+      struct obj *o[scene_size];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* The use count is actually just to help tracking what functions have not exited yet */</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_use_count use_count;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int use_cb(struct osmo_use_count_entry *use_count_entry, int32_t old_use_count, const char *file, int line)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        char buf[128];</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGP(DLGLOBAL, LOGL_DEBUG, "%s\n", osmo_use_count_name_buf(buf, sizeof(buf), use_count_entry->use_count));</span><br><span style="color: hsl(120, 100%, 40%);">+       return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* References to related actual objects that are tied to FSM instances. */</span><br><span style="color: hsl(120, 100%, 40%);">+struct obj {</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_fsm_inst *fi;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct scene *s;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct obj *parent;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct obj *child[2];</span><br><span style="color: hsl(120, 100%, 40%);">+ struct obj *other[3];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void scene_forget_obj(struct scene *s, struct obj *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < ARRAY_SIZE(obj->s->o); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (obj->s->o[i] != obj)</span><br><span style="color: hsl(120, 100%, 40%);">+                        continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGPFSML(obj->fi, LOGL_DEBUG, "scene forgets %s\n", obj->fi->id);</span><br><span style="color: hsl(120, 100%, 40%);">+          obj->s->o[i] = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct scene *g_scene = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define GET() \</span><br><span style="color: hsl(120, 100%, 40%);">+      char *token = talloc_asprintf(g_scene, "%s.%s()", obj->fi->id, __func__); \</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_use_count_get_put(&g_scene->use_count, token, 1)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define PUT() osmo_use_count_get_put(&g_scene->use_count, token, -1)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void alive_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   LOGPFSML(fi, LOGL_DEBUG, "%s()\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Remove obj->other[*] reference, return true if found and removed, false if not. */</span><br><span style="color: hsl(120, 100%, 40%);">+bool other_gone(struct obj *obj, struct obj *other)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        GET();</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < ARRAY_SIZE(obj->other); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (obj->other[i] == other) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      obj->other[i] = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                      LOGPFSML(obj->fi, LOGL_DEBUG, "EV_OTHER_GONE: Dropped reference %s.other[%d] = %s\n", obj->fi->id, i,</span><br><span style="color: hsl(120, 100%, 40%);">+                               other->fi->id);</span><br><span style="color: hsl(120, 100%, 40%);">+                        PUT();</span><br><span style="color: hsl(120, 100%, 40%);">+                        return true;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     PUT();</span><br><span style="color: hsl(120, 100%, 40%);">+        return false;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Remove obj->child[*] reference, return true if more children remain after this, false if all are gone */</span><br><span style="color: hsl(120, 100%, 40%);">+bool child_gone(struct obj *obj, struct obj *child)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        bool found;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!child) {</span><br><span style="color: hsl(120, 100%, 40%);">+         LOGPFSML(obj->fi, LOGL_DEBUG, "EV_CHILD_GONE with NULL data, must be a parent_term event. Ignore.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           return true;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     GET();</span><br><span style="color: hsl(120, 100%, 40%);">+        found = false;</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < ARRAY_SIZE(obj->child); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (obj->child[i] == child) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      obj->child[i] = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                      LOGPFSML(obj->fi, LOGL_DEBUG, "EV_CHILD_GONE: Dropped reference %s.child[%d] = %s\n", obj->fi->id, i,</span><br><span style="color: hsl(120, 100%, 40%);">+                               child->fi->id);</span><br><span style="color: hsl(120, 100%, 40%);">+                        found = true;</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!found)</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGPFSML(obj->fi, LOGL_ERROR, "EV_CHILD_GONE: cannot find child %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                    child && child->fi ? child->fi->id : "(null)");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Any children left? */</span><br><span style="color: hsl(120, 100%, 40%);">+      for (i = 0; i < ARRAY_SIZE(obj->child); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (obj->child[i]) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       LOGPFSML(obj->fi, LOGL_DEBUG, "still exists: child[%d]\n", i);</span><br><span style="color: hsl(120, 100%, 40%);">+                   PUT();</span><br><span style="color: hsl(120, 100%, 40%);">+                        return true;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     LOGPFSML(obj->fi, LOGL_DEBUG, "No more children\n");</span><br><span style="color: hsl(120, 100%, 40%);">+     PUT();</span><br><span style="color: hsl(120, 100%, 40%);">+        return false;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void alive(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct obj *obj = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        GET();</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGPFSML(fi, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_fsm_event_name(fi->fsm, event));</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case EV_OTHER_GONE:</span><br><span style="color: hsl(120, 100%, 40%);">+           if (other_gone(obj, data)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  /* Something this object depends on is gone, trigger deallocation */</span><br><span style="color: hsl(120, 100%, 40%);">+                  osmo_fsm_inst_state_chg(fi, ST_DESTROYING, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      case EV_CHILD_GONE:</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!child_gone(obj, data)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 /* All children are gone. Deallocate. */</span><br><span style="color: hsl(120, 100%, 40%);">+                      osmo_fsm_inst_state_chg(fi, ST_DESTROYING, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      case EV_DESTROY:</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_fsm_inst_state_chg(fi, ST_DESTROYING, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     PUT();</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void destroying_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct obj *obj = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        GET();</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGPFSML(fi, LOGL_DEBUG, "%s() from %s\n", __func__, osmo_fsm_state_name(fi->fsm, prev_state));</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+     PUT();</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void destroying(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct obj *obj = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        GET();</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGPFSML(fi, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_fsm_event_name(fi->fsm, event));</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case EV_OTHER_GONE:</span><br><span style="color: hsl(120, 100%, 40%);">+           other_gone(obj, data);</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      case EV_CHILD_GONE:</span><br><span style="color: hsl(120, 100%, 40%);">+           child_gone(obj, data);</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      case EV_DESTROY:</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGPFSML(fi, LOGL_DEBUG, "already destroying\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     PUT();</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define S(x)       (1 << (x))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct osmo_fsm_state test_fsm_states[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+  [ST_ALIVE] = {</span><br><span style="color: hsl(120, 100%, 40%);">+                .name = "alive",</span><br><span style="color: hsl(120, 100%, 40%);">+            .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(EV_CHILD_GONE)</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(EV_OTHER_GONE)</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(EV_DESTROY)</span><br><span style="color: hsl(120, 100%, 40%);">+                       ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(ST_ALIVE)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(ST_DESTROYING)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .onenter = alive_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+             .action = alive,</span><br><span style="color: hsl(120, 100%, 40%);">+      },</span><br><span style="color: hsl(120, 100%, 40%);">+    [ST_DESTROYING] = {</span><br><span style="color: hsl(120, 100%, 40%);">+           .name = "destroying",</span><br><span style="color: hsl(120, 100%, 40%);">+               .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(EV_CHILD_GONE)</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(EV_OTHER_GONE)</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(EV_DESTROY)</span><br><span style="color: hsl(120, 100%, 40%);">+                       ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(ST_DESTROYING)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .onenter = destroying_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+                .action = destroying,</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct obj *obj = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        GET();</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGPFSML(fi, LOGL_DEBUG, "%s()\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Remove from the scene overview for this test */</span><br><span style="color: hsl(120, 100%, 40%);">+    scene_forget_obj(obj->s, obj);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Signal "other" objects */</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < ARRAY_SIZE(obj->other); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+          struct obj *other = obj->other[i];</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!other)</span><br><span style="color: hsl(120, 100%, 40%);">+                   continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGPFSML(fi, LOGL_DEBUG, "removing reference %s.other[%d] -> %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                       obj->fi->id, i, other->fi->id);</span><br><span style="color: hsl(120, 100%, 40%);">+          obj->other[i] = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_fsm_inst_dispatch(other->fi, EV_OTHER_GONE, obj);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (obj->parent)</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_fsm_inst_dispatch(obj->parent->fi, EV_CHILD_GONE, obj);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* children are handled by fsm.c: term event / osmo_fsm_inst_term_children() */</span><br><span style="color: hsl(120, 100%, 40%);">+       LOGPFSML(fi, LOGL_DEBUG, "%s() done\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+  PUT();</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int timer_cb(struct osmo_fsm_inst *fi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGPFSML(fi, LOGL_DEBUG, "%s()\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+       return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    LOGPFSML(fi, LOGL_DEBUG, "%s()\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fsm test_fsm = {</span><br><span style="color: hsl(120, 100%, 40%);">+    .name = "test",</span><br><span style="color: hsl(120, 100%, 40%);">+     .states = test_fsm_states,</span><br><span style="color: hsl(120, 100%, 40%);">+    .num_states = ARRAY_SIZE(test_fsm_states),</span><br><span style="color: hsl(120, 100%, 40%);">+    .cleanup = cleanup,</span><br><span style="color: hsl(120, 100%, 40%);">+   .timer_cb = timer_cb,</span><br><span style="color: hsl(120, 100%, 40%);">+ .event_names = test_fsm_event_names,</span><br><span style="color: hsl(120, 100%, 40%);">+  .pre_term = pre_term,</span><br><span style="color: hsl(120, 100%, 40%);">+ .log_subsys = DLGLOBAL,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *ctx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct obj *obj_alloc(struct scene *s, struct obj *parent, const char *id) {</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_fsm_inst *fi;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct obj *obj;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!parent) {</span><br><span style="color: hsl(120, 100%, 40%);">+                fi = osmo_fsm_inst_alloc(&test_fsm, s, NULL, LOGL_DEBUG, id);</span><br><span style="color: hsl(120, 100%, 40%);">+             OSMO_ASSERT(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+      } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              fi = osmo_fsm_inst_alloc_child(&test_fsm, parent->fi, EV_CHILD_GONE);</span><br><span style="color: hsl(120, 100%, 40%);">+          OSMO_ASSERT(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_fsm_inst_update_id(fi, id);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   obj = talloc_zero(fi, struct obj);</span><br><span style="color: hsl(120, 100%, 40%);">+    fi->priv = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+    *obj = (struct obj){</span><br><span style="color: hsl(120, 100%, 40%);">+          .fi = fi,</span><br><span style="color: hsl(120, 100%, 40%);">+                     .s = s,</span><br><span style="color: hsl(120, 100%, 40%);">+                       .parent = parent,</span><br><span style="color: hsl(120, 100%, 40%);">+     };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (parent) {</span><br><span style="color: hsl(120, 100%, 40%);">+         int i;</span><br><span style="color: hsl(120, 100%, 40%);">+                for (i = 0; i < ARRAY_SIZE(parent->child); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (parent->child[i])</span><br><span style="color: hsl(120, 100%, 40%);">+                              continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                     parent->child[i] = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+                    break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return obj;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void obj_add_other(struct obj *a, struct obj *b)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < ARRAY_SIZE(a->other); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (a->other[i])</span><br><span style="color: hsl(120, 100%, 40%);">+                   i++;</span><br><span style="color: hsl(120, 100%, 40%);">+          a->other[i] = b;</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGPFSML(a->fi, LOGL_DEBUG, "%s.other[%d] = %s\n", a->fi->id, i, b->fi->id);</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void obj_set_other(struct obj *a, struct obj *b)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   obj_add_other(a, b);</span><br><span style="color: hsl(120, 100%, 40%);">+  obj_add_other(b, a);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct scene *scene_alloc()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct scene *s = talloc_zero(ctx, struct scene);</span><br><span style="color: hsl(120, 100%, 40%);">+     s->use_count.talloc_object = s;</span><br><span style="color: hsl(120, 100%, 40%);">+    s->use_count.use_cb = use_cb;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    LOGP(DLGLOBAL, LOGL_DEBUG, "%s()\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+    s->o[root] = obj_alloc(s, NULL, "root");</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  s->o[branch0] = obj_alloc(s, s->o[root], "_branch0");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       s->o[twig0a] = obj_alloc(s, s->o[branch0], "__twig0a");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /*</span><br><span style="color: hsl(120, 100%, 40%);">+    s->o[twig0b] = obj_alloc(s, s->o[branch0], "__twig0b");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     s->o[branch1] = obj_alloc(s, s->o[root], "_branch1");</span><br><span style="color: hsl(120, 100%, 40%);">+ s->o[twig1a] = obj_alloc(s, s->o[branch1], "__twig1a");</span><br><span style="color: hsl(120, 100%, 40%);">+       s->o[twig1b] = obj_alloc(s, s->o[branch1], "__twig1b");</span><br><span style="color: hsl(120, 100%, 40%);">+       */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  s->o[other] = obj_alloc(s, NULL, "other");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     obj_set_other(s->o[branch0], s->o[other]);</span><br><span style="color: hsl(120, 100%, 40%);">+      obj_set_other(s->o[twig0a], s->o[other]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return s;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int scene_dump(struct scene *s)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        int got = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  for (i = 0; i < ARRAY_SIZE(s->o); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!s->o[i])</span><br><span style="color: hsl(120, 100%, 40%);">+                      continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGP(DLGLOBAL, LOGL_DEBUG, "  %s\n", s->o[i]->fi->id);</span><br><span style="color: hsl(120, 100%, 40%);">+             got++;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     return got;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void scene_clean(struct scene *s)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < ARRAY_SIZE(s->o); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!s->o[i])</span><br><span style="color: hsl(120, 100%, 40%);">+                      continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_fsm_inst_term(s->o[i]->fi, OSMO_FSM_TERM_ERROR, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                s->o[i] = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     talloc_free(s);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void obj_destroy(struct obj *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_fsm_inst_dispatch(obj->fi, EV_DESTROY, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void obj_term(struct obj *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_fsm_inst_term(obj->fi, OSMO_FSM_TERM_REGULAR, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void test_dealloc(enum objname trigger, bool by_destroy_event)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct scene *s = scene_alloc();</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *label = by_destroy_event ? "destroy-event" : "term";</span><br><span style="color: hsl(120, 100%, 40%);">+  int remain;</span><br><span style="color: hsl(120, 100%, 40%);">+   g_scene = s;</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!s->o[trigger]) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DLGLOBAL, LOGL_DEBUG, "--- Test disabled: object %d was not created. Cleaning up.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+               trigger);</span><br><span style="color: hsl(120, 100%, 40%);">+                scene_clean(s);</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     LOGP(DLGLOBAL, LOGL_DEBUG, "------ before %s cascade, got:\n", label);</span><br><span style="color: hsl(120, 100%, 40%);">+      scene_dump(s);</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGP(DLGLOBAL, LOGL_DEBUG, "---\n");</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGP(DLGLOBAL, LOGL_DEBUG, "--- %s at %s\n", label, s->o[trigger]->fi->id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (by_destroy_event)</span><br><span style="color: hsl(120, 100%, 40%);">+         obj_destroy(s->o[trigger]);</span><br><span style="color: hsl(120, 100%, 40%);">+        else</span><br><span style="color: hsl(120, 100%, 40%);">+          obj_term(s->o[trigger]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGP(DLGLOBAL, LOGL_DEBUG, "--- after %s cascade:\n", label);</span><br><span style="color: hsl(120, 100%, 40%);">+       remain = scene_dump(s);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (remain) {</span><br><span style="color: hsl(120, 100%, 40%);">+         LOGP(DLGLOBAL, LOGL_DEBUG, "--- %d objects remain. cleaning up\n", remain);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGP(DLGLOBAL, LOGL_DEBUG, "--- all deallocated.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+       scene_clean(s);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int main(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       enum objname trigger;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t ctx_blocks;</span><br><span style="color: hsl(120, 100%, 40%);">+    size_t ctx_size;</span><br><span style="color: hsl(120, 100%, 40%);">+      int by_destroy_event;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ctx = talloc_named_const(NULL, 0, "main");</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_init_logging2(ctx, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      log_set_print_filename(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        log_set_print_level(osmo_stderr_target, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+   log_set_print_category(osmo_stderr_target, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+        log_set_print_category_hex(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+    log_set_use_color(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_fsm_log_addr(false);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   log_set_category_filter(osmo_stderr_target, DLGLOBAL, 1, LOGL_DEBUG);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_fsm_register(&test_fsm);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ctx_blocks = talloc_total_blocks(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+        ctx_size = talloc_total_size(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  for (trigger = 0; trigger < scene_size; trigger++) {</span><br><span style="color: hsl(120, 100%, 40%);">+               for (by_destroy_event = 0; by_destroy_event < 2; by_destroy_event++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     test_dealloc(trigger, (bool)by_destroy_event);</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (ctx_blocks != talloc_total_blocks(ctx)</span><br><span style="color: hsl(120, 100%, 40%);">+                        || ctx_size != talloc_total_size(ctx)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          talloc_report_full(ctx, stderr);</span><br><span style="color: hsl(120, 100%, 40%);">+                              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+                   }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   talloc_free(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/fsm/fsm_dealloc_test.err b/tests/fsm/fsm_dealloc_test.err</span><br><span>new file mode 100644</span><br><span>index 0000000..1719677</span><br><span>--- /dev/null</span><br><span>+++ b/tests/fsm/fsm_dealloc_test.err</span><br><span>@@ -0,0 +1,122 @@</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG scene_alloc()</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){alive}: Allocated</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){alive}: Allocated</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){alive}: is child of test(_branch0)</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){alive}: Allocated</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){alive}: _branch0.other[0] = other</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){alive}: other.other[0] = _branch0</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(__twig0a){alive}: __twig0a.other[0] = other</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){alive}: other.other[1] = __twig0a</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG --- Test disabled: object 0 was not created. Cleaning up.</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){alive}: Terminating (cause = OSMO_FSM_TERM_ERROR)</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){alive}: pre_term()</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(__twig0a){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT)</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(__twig0a){alive}: pre_term()</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(__twig0a){alive}: Removing from parent test(_branch0)</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 1 (__twig0a.cleanup())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(__twig0a){alive}: cleanup()</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(__twig0a){alive}: scene forgets __twig0a</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(__twig0a){alive}: removing reference __twig0a.other[0] -> other</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){alive}: Received Event EV_OTHER_GONE</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 2 (__twig0a.cleanup(),other.alive())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){alive}: alive(EV_OTHER_GONE)</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 3 (__twig0a.cleanup(),other.alive(),other.other_gone())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){alive}: EV_OTHER_GONE: Dropped reference other.other[1] = __twig0a</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 2 (__twig0a.cleanup(),other.alive())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){alive}: state_chg to destroying</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 3 (__twig0a.cleanup(),other.alive(),other.destroying_onenter())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){destroying}: destroying_onenter() from alive</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){destroying}: Terminating (cause = OSMO_FSM_TERM_REGULAR)</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){destroying}: pre_term()</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 4 (__twig0a.cleanup(),other.alive(),other.destroying_onenter(),other.cleanup())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){destroying}: cleanup()</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){destroying}: scene forgets other</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){destroying}: removing reference other.other[0] -> _branch0</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_OTHER_GONE</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 5 (__twig0a.cleanup(),other.alive(),other.destroying_onenter(),other.cleanup(),_branch0.alive())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_OTHER_GONE)</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 6 (__twig0a.cleanup(),other.alive(),other.destroying_onenter(),other.cleanup(),_branch0.alive(),_branch0.other_gone())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){alive}: EV_OTHER_GONE: Dropped reference _branch0.other[0] = other</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 5 (__twig0a.cleanup(),other.alive(),other.destroying_onenter(),other.cleanup(),_branch0.alive())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){alive}: state_chg to destroying</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 6 (__twig0a.cleanup(),other.alive(),other.destroying_onenter(),other.cleanup(),_branch0.alive(),_branch0.destroying_onenter())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){destroying}: destroying_onenter() from alive</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){destroying}: Terminating (cause = OSMO_FSM_TERM_REGULAR)</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){destroying}: pre_term()</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 7 (__twig0a.cleanup(),other.alive(),other.destroying_onenter(),other.cleanup(),_branch0.alive(),_branch0.destroying_onenter(),_</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){destroying}: cleanup()</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){destroying}: scene forgets _branch0</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){destroying}: cleanup() done</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 6 (__twig0a.cleanup(),other.alive(),other.destroying_onenter(),other.cleanup(),_branch0.alive(),_branch0.destroying_onenter())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){destroying}: Freeing instance</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(_branch0){destroying}: Deallocated</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 5 (__twig0a.cleanup(),other.alive(),other.destroying_onenter(),other.cleanup(),_branch0.alive())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 4 (__twig0a.cleanup(),other.alive(),other.destroying_onenter(),other.cleanup())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){destroying}: cleanup() done</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG 3 (__twig0a.cleanup(),other.alive(),other.destroying_onenter())</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){destroying}: Freeing instance</span><br><span style="color: hsl(120, 100%, 40%);">+DLGLOBAL DEBUG test(other){destroying}: Deallocated</span><br><span style="color: hsl(120, 100%, 40%);">+=================================================================</span><br><span style="color: hsl(120, 100%, 40%);">+==12545==ERROR: AddressSanitizer: heap-use-after-free on address 0x6120000003a8 at pc 0x7fa96fdc9149 bp 0x7fff6045b000 sp 0x7fff6045aff8</span><br><span style="color: hsl(120, 100%, 40%);">+WRITE of size 8 at 0x6120000003a8 thread T0</span><br><span style="color: hsl(120, 100%, 40%);">+    #0 0x7fa96fdc9148 in __llist_del ../../../src/libosmocore/include/osmocom/core/linuxlist.h:114</span><br><span style="color: hsl(120, 100%, 40%);">+    #1 0x7fa96fdc9280 in llist_del ../../../src/libosmocore/include/osmocom/core/linuxlist.h:126</span><br><span style="color: hsl(120, 100%, 40%);">+    #2 0x7fa96fdcddaa in osmo_fsm_inst_free ../../../src/libosmocore/src/fsm.c:404</span><br><span style="color: hsl(120, 100%, 40%);">+    #3 0x7fa96fdd599c in _osmo_fsm_inst_term ../../../src/libosmocore/src/fsm.c:738</span><br><span style="color: hsl(120, 100%, 40%);">+    #4 0x55dde97cb9e3 in destroying_onenter ../../../src/libosmocore/tests/fsm/fsm_dealloc_test.c:177</span><br><span style="color: hsl(120, 100%, 40%);">+    #5 0x7fa96fdd26be in state_chg ../../../src/libosmocore/src/fsm.c:521</span><br><span style="color: hsl(120, 100%, 40%);">+    #6 0x7fa96fdd2770 in _osmo_fsm_inst_state_chg ../../../src/libosmocore/src/fsm.c:577</span><br><span style="color: hsl(120, 100%, 40%);">+    #7 0x55dde97cb2e6 in alive ../../../src/libosmocore/tests/fsm/fsm_dealloc_test.c:151</span><br><span style="color: hsl(120, 100%, 40%);">+    #8 0x7fa96fdd3d2f in _osmo_fsm_inst_dispatch ../../../src/libosmocore/src/fsm.c:685</span><br><span style="color: hsl(120, 100%, 40%);">+    #9 0x55dde97cd0ee in cleanup ../../../src/libosmocore/tests/fsm/fsm_dealloc_test.c:255</span><br><span style="color: hsl(120, 100%, 40%);">+    #10 0x7fa96fdd5192 in _osmo_fsm_inst_term ../../../src/libosmocore/src/fsm.c:733</span><br><span style="color: hsl(120, 100%, 40%);">+    #11 0x7fa96fdd60b1 in _osmo_fsm_inst_term_children ../../../src/libosmocore/src/fsm.c:784</span><br><span style="color: hsl(120, 100%, 40%);">+    #12 0x7fa96fdd475e in _osmo_fsm_inst_term ../../../src/libosmocore/src/fsm.c:720</span><br><span style="color: hsl(120, 100%, 40%);">+    #13 0x55dde97cf5d5 in scene_clean ../../../src/libosmocore/tests/fsm/fsm_dealloc_test.c:392</span><br><span style="color: hsl(120, 100%, 40%);">+    #14 0x55dde97cf92f in test_dealloc ../../../src/libosmocore/tests/fsm/fsm_dealloc_test.c:417</span><br><span style="color: hsl(120, 100%, 40%);">+    #15 0x55dde97cfffe in main ../../../src/libosmocore/tests/fsm/fsm_dealloc_test.c:465</span><br><span style="color: hsl(120, 100%, 40%);">+    #16 0x7fa96eff409a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a)</span><br><span style="color: hsl(120, 100%, 40%);">+    #17 0x55dde97c7329 in _start (/n/s/dev/make/libosmocore/tests/fsm/fsm_dealloc_test+0x11329)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+0x6120000003a8 is located 104 bytes inside of 288-byte region [0x612000000340,0x612000000460)</span><br><span style="color: hsl(120, 100%, 40%);">+freed by thread T0 here:</span><br><span style="color: hsl(120, 100%, 40%);">+    #0 0x7fa970061b50 in free (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xe8b50)</span><br><span style="color: hsl(120, 100%, 40%);">+    #1 0x7fa96fcbd5d2  (/usr/lib/x86_64-linux-gnu/libtalloc.so.2+0xb5d2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+previously allocated by thread T0 here:</span><br><span style="color: hsl(120, 100%, 40%);">+    #0 0x7fa970061ed0 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xe8ed0)</span><br><span style="color: hsl(120, 100%, 40%);">+    #1 0x7fa96fcbb140 in _talloc_zero (/usr/lib/x86_64-linux-gnu/libtalloc.so.2+0x9140)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+SUMMARY: AddressSanitizer: heap-use-after-free ../../../src/libosmocore/include/osmocom/core/linuxlist.h:114 in __llist_del</span><br><span style="color: hsl(120, 100%, 40%);">+Shadow bytes around the buggy address:</span><br><span style="color: hsl(120, 100%, 40%);">+  0x0c247fff8020: 00 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa</span><br><span style="color: hsl(120, 100%, 40%);">+  0x0c247fff8030: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd</span><br><span style="color: hsl(120, 100%, 40%);">+  0x0c247fff8040: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd</span><br><span style="color: hsl(120, 100%, 40%);">+  0x0c247fff8050: fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa</span><br><span style="color: hsl(120, 100%, 40%);">+  0x0c247fff8060: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd</span><br><span style="color: hsl(120, 100%, 40%);">+=>0x0c247fff8070: fd fd fd fd fd[fd]fd fd fd fd fd fd fd fd fd fd</span><br><span style="color: hsl(120, 100%, 40%);">+  0x0c247fff8080: fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa</span><br><span style="color: hsl(120, 100%, 40%);">+  0x0c247fff8090: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00</span><br><span style="color: hsl(120, 100%, 40%);">+  0x0c247fff80a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</span><br><span style="color: hsl(120, 100%, 40%);">+  0x0c247fff80b0: 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa fa</span><br><span style="color: hsl(120, 100%, 40%);">+  0x0c247fff80c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span style="color: hsl(120, 100%, 40%);">+Shadow byte legend (one shadow byte represents 8 application bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+  Addressable:           00</span><br><span style="color: hsl(120, 100%, 40%);">+  Partially addressable: 01 02 03 04 05 06 07 </span><br><span style="color: hsl(120, 100%, 40%);">+  Heap left redzone:       fa</span><br><span style="color: hsl(120, 100%, 40%);">+  Freed heap region:       fd</span><br><span style="color: hsl(120, 100%, 40%);">+  Stack left redzone:      f1</span><br><span style="color: hsl(120, 100%, 40%);">+  Stack mid redzone:       f2</span><br><span style="color: hsl(120, 100%, 40%);">+  Stack right redzone:     f3</span><br><span style="color: hsl(120, 100%, 40%);">+  Stack after return:      f5</span><br><span style="color: hsl(120, 100%, 40%);">+  Stack use after scope:   f8</span><br><span style="color: hsl(120, 100%, 40%);">+  Global redzone:          f9</span><br><span style="color: hsl(120, 100%, 40%);">+  Global init order:       f6</span><br><span style="color: hsl(120, 100%, 40%);">+  Poisoned by user:        f7</span><br><span style="color: hsl(120, 100%, 40%);">+  Container overflow:      fc</span><br><span style="color: hsl(120, 100%, 40%);">+  Array cookie:            ac</span><br><span style="color: hsl(120, 100%, 40%);">+  Intra object redzone:    bb</span><br><span style="color: hsl(120, 100%, 40%);">+  ASan internal:           fe</span><br><span style="color: hsl(120, 100%, 40%);">+  Left alloca redzone:     ca</span><br><span style="color: hsl(120, 100%, 40%);">+  Right alloca redzone:    cb</span><br><span style="color: hsl(120, 100%, 40%);">+==12545==ABORTING</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/13391">change 13391</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/13391"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: If801907c541bca9f524c9e5fd22ac280ca16979a </div>
<div style="display:none"> Gerrit-Change-Number: 13391 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>