Change in libosmocore[master]: add fsm_dealloc_test.c

Neels Hofmeyr gerrit-no-reply at lists.osmocom.org
Thu Apr 11 05:36:37 UTC 2019


Neels Hofmeyr has submitted this change and it was merged. ( https://gerrit.osmocom.org/13391 )

Change subject: add fsm_dealloc_test.c
......................................................................

add fsm_dealloc_test.c

Despite efforts to properly handle "GONE" events and entering a ST_DESTROYING
only once, so far this test runs straight into a heap use-after-free. With
current fsm.c, it is hard to resolve the situation with the objects named
"other" also causing deallocations besides the FSM instance parent/child
relations.

For illustration, add an "expected" test output file fsm_dealloc_test.err,
making this pass will follow in a subsequent patch.

Change-Id: If801907c541bca9f524c9e5fd22ac280ca16979a
---
M tests/Makefile.am
A tests/fsm/fsm_dealloc_test.c
A tests/fsm/fsm_dealloc_test.err
3 files changed, 606 insertions(+), 1 deletion(-)

Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified



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

-- 
To view, visit https://gerrit.osmocom.org/13391
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: If801907c541bca9f524c9e5fd22ac280ca16979a
Gerrit-Change-Number: 13391
Gerrit-PatchSet: 6
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder (1000002)
Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr at sysmocom.de>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190411/c2b8ce9c/attachment.html>


More information about the gerrit-log mailing list