sgsn: Add test that exposes a dangling pointer to the LLME

On detach the LLME get's unassigned (and hence destroyed) but the
GMM context will still point to that dead structure.
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index 6224fd5..30ed1d9 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -21,6 +21,7 @@
 
 #include <openbsc/gprs_llc.h>
 #include <openbsc/sgsn.h>
+#include <openbsc/gprs_gmm.h>
 #include <openbsc/debug.h>
 
 #include <osmocom/gsm/gsm_utils.h>
@@ -53,6 +54,18 @@
 	return count;		
 }
 
+static struct msgb *create_msg(const uint8_t *data, size_t len)
+{
+	struct msgb *msg = msgb_alloc(len + 8, "test message");
+	msg->l1h = msgb_put(msg, 8);
+	msg->l2h = msgb_put(msg, len);
+	memcpy(msg->l2h, data, len);
+
+	msgb_bcid(msg) = msg->l1h;
+	msgb_gmmh(msg) = msg->l2h;
+	return msg;
+}
+
 static void test_llme(void)
 {
 	struct gprs_llc_lle *lle, *lle_copy;
@@ -86,6 +99,52 @@
 	OSMO_ASSERT(count(gprs_llme_list()) == 0);
 }
 
+/*
+ * Test that a GMM Detach will remove the MMCTX and the
+ * associated LLME.
+ */
+static void test_gmm_detach(void)
+{
+	struct gprs_ra_id raid = { 0, };
+	struct sgsn_mm_ctx *ctx, *ictx;
+	struct gprs_llc_lle *lle;
+	uint32_t local_tlli;
+	struct msgb *msg;
+
+	printf("Testing GMM detach\n");
+
+	/* DTAP - Detach Request (MO) */
+	/* normal detach, power_off = 0 */
+	static const unsigned char detach_req[] = {
+		0x08, 0x05, 0x01, 0x18, 0x05, 0xf4, 0xef, 0xe2,
+		0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb
+	};
+
+	/* Create a conext and search for it */
+	OSMO_ASSERT(count(gprs_llme_list()) == 0);
+	local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL);
+	lle = gprs_lle_get_or_create(local_tlli, 3);
+	ctx = sgsn_mm_ctx_alloc(local_tlli, &raid);
+	ctx->mm_state = GMM_REGISTERED_NORMAL;
+	ctx->llme = lle->llme;
+
+	ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid);
+	OSMO_ASSERT(ictx == ctx);
+	OSMO_ASSERT(count(gprs_llme_list()) == 1);
+
+	/* inject the detach */
+	msg = create_msg(detach_req, ARRAY_SIZE(detach_req));
+	msgb_tlli(msg) = local_tlli;
+	gsm0408_gprs_rcvmsg(msg, ctx->llme);
+	msgb_free(msg);
+
+	/* verify that things are gone */
+	OSMO_ASSERT(count(gprs_llme_list()) == 0);
+	ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid);
+	/* this is still wrong and needs to be changed */
+	OSMO_ASSERT(ictx);
+	OSMO_ASSERT(ictx->llme == lle->llme);
+}
 
 static struct log_info_cat gprs_categories[] = {
 	[DMM] = {
@@ -149,6 +208,7 @@
 	tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb");
 
 	test_llme();
+	test_gmm_detach();
 	printf("Done\n");
 	return 0;
 }