[PATCH 10/10] emu: Add a crash re-producer for the SGSN (and the concept of tests)

Holger Freyther hfreyther at sysmocom.de
Thu Aug 22 07:17:04 UTC 2013


From: Holger Hans Peter Freyther <holger at moiji-mobile.com>

Introduce the concept of tests that will be ran one after the other.
This new test will send static message that will lead to the opening
of a PDP context. At this point one should use ping with a large
packet size and suspend/stop the emulator. Once the NS connection is
considered dead the SGSN will crash with a double free.

Reproduce:
0.) Add IMSI 901700000003094 to the ACL
1.) Stop/Suspend the emulation process so the NS Alive times out
2.) Use ping IP -s 2048

This will create a double free...

 #4  0xb7bb2646 in talloc_abort_double_free () at talloc.c:175
 #5  0xb7bbd41a in talloc_chunk_from_ptr (ptr=0x8091208) at talloc.c:190
 #6  _talloc_free (ptr=0x8091208) at talloc.c:517
 #7  talloc_free (ptr=ptr at entry=0x8091208) at talloc.c:990
 #8  0xb7bb319b in msgb_free (m=m at entry=0x8091208) at msgb.c:72
 #9  0x0804db54 in sndcp_send_ud_frag (fs=0xbfffcc6c) at gprs_sndcp.c:423
 #10 sndcp_unitdata_req (msg=msg at entry=0x808eed8, lle=0x808fbc8, nsapi=5 '\005',
    mmcontext=mmcontext at entry=0x80903e8) at gprs_sndcp.c:471
---
 tests/Makefile.am                     |   3 +-
 tests/emu/gprs_tests.h                |  56 +++++++++++
 tests/emu/openbsc_clone.c             |  19 ++++
 tests/emu/openbsc_clone.h             |  32 +++++++
 tests/emu/pcu_emu.cpp                 |  47 +++++++++-
 tests/emu/test_pdp_activation.cpp     | 172 ++++++++++++++++++++++++++++++++++
 tests/emu/test_replay_gprs_attach.cpp |  15 +--
 7 files changed, 330 insertions(+), 14 deletions(-)
 create mode 100644 tests/emu/gprs_tests.h
 create mode 100644 tests/emu/test_pdp_activation.cpp

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0131e47..88be652 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -10,7 +10,8 @@ rlcmac_RLCMACTest_LDADD = \
 	$(COMMON_LA)
 
 emu_pcu_emu_SOURCES = emu/pcu_emu.cpp emu/test_replay_gprs_attach.cpp \
-	emu/openbsc_clone.c emu/openbsc_clone.h
+	emu/openbsc_clone.c emu/openbsc_clone.h emu/gprs_tests.h \
+	emu/test_pdp_activation.cpp
 emu_pcu_emu_LDADD = \
 	$(top_builddir)/src/libgprs.la \
 	$(LIBOSMOGB_LIBS) \
diff --git a/tests/emu/gprs_tests.h b/tests/emu/gprs_tests.h
new file mode 100644
index 0000000..eeb4f82
--- /dev/null
+++ b/tests/emu/gprs_tests.h
@@ -0,0 +1,56 @@
+/* (C) 2013 by Holger Hans Peter Freyther
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef tests_h
+#define tests_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <osmocom/core/msgb.h>
+#include <string.h>
+
+struct gprs_bssgp_pcu;
+struct tlv_parsed;
+struct msgb;
+
+struct gprs_test {
+	const char *name;
+	const char *description;
+	void (*start)(struct gprs_bssgp_pcu *);
+	void (*data) (struct gprs_bssgp_pcu *, struct msgb *, struct tlv_parsed *);
+};
+
+void gprs_test_success(struct gprs_bssgp_pcu *);
+
+static inline struct msgb *create_msg(const uint8_t *data, size_t len)
+{
+	struct msgb *msg = msgb_alloc_headroom(4096, 128, "create msg");
+	msg->l3h = msgb_put(msg, len);
+	memcpy(msg->l3h, data, len);
+	return msg;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/tests/emu/openbsc_clone.c b/tests/emu/openbsc_clone.c
index 707819b..b1052ff 100644
--- a/tests/emu/openbsc_clone.c
+++ b/tests/emu/openbsc_clone.c
@@ -21,6 +21,7 @@
 #include <gprs_debug.h>
 
 #include <osmocom/core/utils.h>
+#include <osmocom/gsm/tlv.h>
 
 #include <errno.h>
 
@@ -214,3 +215,21 @@ int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp,
 
 	return 0;
 }
+
+const struct tlv_definition gsm48_gmm_att_tlvdef = {
+	.def = {
+		[GSM48_IE_GMM_CIPH_CKSN]	= { TLV_TYPE_FIXED, 1 },
+		[GSM48_IE_GMM_TIMER_READY]	= { TLV_TYPE_TV, 1 },
+		[GSM48_IE_GMM_ALLOC_PTMSI]	= { TLV_TYPE_TLV, 0 },
+		[GSM48_IE_GMM_PTMSI_SIG]	= { TLV_TYPE_FIXED, 3 },
+		[GSM48_IE_GMM_AUTH_RAND]	= { TLV_TYPE_FIXED, 16 },
+		[GSM48_IE_GMM_AUTH_SRES]	= { TLV_TYPE_FIXED, 4 },
+		[GSM48_IE_GMM_IMEISV]		= { TLV_TYPE_TLV, 0 },
+		[GSM48_IE_GMM_DRX_PARAM]	= { TLV_TYPE_FIXED, 2 },
+		[GSM48_IE_GMM_MS_NET_CAPA]	= { TLV_TYPE_TLV, 0 },
+		[GSM48_IE_GMM_PDP_CTX_STATUS]	= { TLV_TYPE_TLV, 0 },
+		[GSM48_IE_GMM_PS_LCS_CAPA]	= { TLV_TYPE_TLV, 0 },
+		[GSM48_IE_GMM_GMM_MBMS_CTX_ST]	= { TLV_TYPE_TLV, 0 },
+	},
+};
+
diff --git a/tests/emu/openbsc_clone.h b/tests/emu/openbsc_clone.h
index d62ff22..b3cc033 100644
--- a/tests/emu/openbsc_clone.h
+++ b/tests/emu/openbsc_clone.h
@@ -23,6 +23,8 @@
 extern "C" {
 #endif
 
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
 #include <stdint.h>
 
 enum gprs_llc_cmd {
@@ -57,6 +59,36 @@ struct gprs_llc_hdr_parsed {
 
 int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp, const uint8_t *llc_hdr, int len);
 
+/* Table 10.4 / 10.4a, GPRS Mobility Management (GMM) */
+#define GSM48_MT_GMM_ATTACH_ACK		0x02
+
+/* Chapter 9.4.2 / Table 9.4.2 */
+struct gsm48_attach_ack {
+	uint8_t att_result:4,	/* 10.5.5.7 */
+		 force_stby:4;	/* 10.5.5.1 */
+	uint8_t ra_upd_timer;	/* 10.5.7.3 */
+	uint8_t radio_prio;	/* 10.5.7.2 */
+	struct gsm48_ra_id ra_id; /* 10.5.5.15 */
+	uint8_t data[0];
+} __attribute__((packed));
+
+enum gsm48_gprs_ie_mm {
+	GSM48_IE_GMM_CIPH_CKSN		= 0x08, /* 10.5.1.2 */
+	GSM48_IE_GMM_TIMER_READY	= 0x17,	/* 10.5.7.3 */
+	GSM48_IE_GMM_ALLOC_PTMSI	= 0x18,	/* 10.5.1.4 */
+	GSM48_IE_GMM_PTMSI_SIG		= 0x19,	/* 10.5.5.8 */
+	GSM48_IE_GMM_AUTH_RAND		= 0x21,	/* 10.5.3.1 */
+	GSM48_IE_GMM_AUTH_SRES		= 0x22,	/* 10.5.3.2 */
+	GSM48_IE_GMM_IMEISV		= 0x23,	/* 10.5.1.4 */
+	GSM48_IE_GMM_DRX_PARAM		= 0x27,	/* 10.5.5.6 */
+	GSM48_IE_GMM_MS_NET_CAPA	= 0x31,	/* 10.5.5.12 */
+	GSM48_IE_GMM_PDP_CTX_STATUS	= 0x32,	/* 10.5.7.1 */
+	GSM48_IE_GMM_PS_LCS_CAPA	= 0x33,	/* 10.5.5.22 */
+	GSM48_IE_GMM_GMM_MBMS_CTX_ST	= 0x35,	/* 10.5.7.6 */
+};
+
+extern const struct tlv_definition gsm48_gmm_att_tlvdef;
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/tests/emu/pcu_emu.cpp b/tests/emu/pcu_emu.cpp
index f4d38cd..4731a60 100644
--- a/tests/emu/pcu_emu.cpp
+++ b/tests/emu/pcu_emu.cpp
@@ -24,6 +24,9 @@ extern "C" {
 #include <pcu_vty.h>
 }
 
+#include "gprs_tests.h"
+
+
 #include <gprs_bssgp_pcu.h>
 #include <gprs_rlcmac.h>
 
@@ -31,6 +34,8 @@ extern "C" {
 #include <sys/types.h>
 #include <sys/socket.h>
 
+static int current_test;
+
 /* Extern data to please the underlying code */
 void *tall_pcu_ctx;
 struct gprs_rlcmac_bts *gprs_rlcmac_bts;
@@ -39,6 +44,29 @@ int16_t spoof_mnc = 0, spoof_mcc = 0;
 extern void test_replay_gprs_attach(struct gprs_bssgp_pcu *pcu);
 extern void test_replay_gprs_data(struct gprs_bssgp_pcu *, struct msgb *, struct tlv_parsed *);
 
+extern void test_pdp_activation_start(struct gprs_bssgp_pcu *pcu);
+extern void test_pdp_activation_data(struct gprs_bssgp_pcu *, struct msgb *, struct tlv_parsed*);
+
+
+struct gprs_test all_tests[] = {
+	{
+		.name		= "gprs_attach_with_tmsi",
+		.description	= "A simple test that verifies that N(U) is "
+				"increasing across various messages. This makes "
+				"sure that no new LLE/LLME is created on the fly.",
+		.start		= test_replay_gprs_attach,
+		.data		= test_replay_gprs_data,
+	},
+	{
+		.name		= "gprs_full_attach_pdp_activation",
+		.description	= "A simple test to do a GPRS attach and open a PDP "
+				"context. Then goes to sleep and waits for you to ping "
+				"the connection and hopefully re-produce a crash.",
+		.start		= test_pdp_activation_start,
+		.data		= test_pdp_activation_data,
+	},
+};
+
 struct gprs_rlcmac_bts *create_bts()
 {
 	struct gprs_rlcmac_bts *bts;
@@ -68,12 +96,12 @@ struct gprs_rlcmac_bts *create_bts()
 static void bvci_unblocked(struct gprs_bssgp_pcu *pcu)
 {
 	printf("BVCI unblocked. We can begin with test cases.\n");
-	test_replay_gprs_attach(pcu);
+	all_tests[current_test].start(pcu);	
 }
 
 static void bssgp_data(struct gprs_bssgp_pcu *pcu, struct msgb *msg, struct tlv_parsed *tp)
 {
-	test_replay_gprs_data(pcu, msg, tp);
+	all_tests[current_test].data(pcu, msg, tp);
 }
 
 void create_and_connect_bssgp(struct gprs_rlcmac_bts *bts,
@@ -112,6 +140,21 @@ int main(int argc, char **argv)
 	return EXIT_SUCCESS;
 }
 
+
+/*
+ * Test handling..
+ */
+void gprs_test_success(struct gprs_bssgp_pcu *pcu)
+{
+	current_test += 1;
+	if (current_test >= ARRAY_SIZE(all_tests)) {
+		printf("All tests executed.\n");
+		exit(EXIT_SUCCESS);
+	}
+
+	all_tests[current_test].start(pcu);
+}
+
 /*
  * stubs that should not be reached
  */
diff --git a/tests/emu/test_pdp_activation.cpp b/tests/emu/test_pdp_activation.cpp
new file mode 100644
index 0000000..909b308
--- /dev/null
+++ b/tests/emu/test_pdp_activation.cpp
@@ -0,0 +1,172 @@
+/* (C) 2013 by Holger Hans Peter Freyther
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+extern "C" {
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/backtrace.h>
+#include <osmocom/gsm/gsm_utils.h>
+}
+
+#include "openbsc_clone.h"
+#include "gprs_tests.h"
+
+#include <gprs_bssgp_pcu.h>
+
+#include <stdint.h>
+#include <string.h>
+
+static const uint8_t attach[] = {
+	0x0e, 0x00, 0x26,
+	0x01, 0xc0, 0x01, 0x08, 0x01, 0x02, 0xe5, 0x80,
+	0x71, 0x0d, 0x01, 0x05, 0xf4, 0x02, 0x30, 0xef,
+	0x0e, 0x09, 0xf1, 0x07, 0x00, 0x01, 0x00, 0x0b,
+	0x34, 0xc7, 0x03, 0x2a, 0xa0, 0x42, 0x7c, 0xad,
+	0xe1, 0x18, 0x0b, 0xf8, 0xef, 0xfc
+};
+
+static const uint8_t id_resp_imei[] = {
+	0x0e, 0x00, 0x11,
+	0x01, 0xc0, 0x05, 0x08, 0x16, 0x08, 0x3a, 0x49,
+	0x50, 0x13, 0x28, 0x15, 0x80, 0x01, 0x21, 0x6c,
+	0x22
+};
+
+static const uint8_t id_resp_imsi[] = {
+	0x0e, 0x00, 0x11,
+	0x01, 0xc0, 0x09, 0x08, 0x16, 0x08, 0x99, 0x10,
+	0x07, 0x00, 0x00, 0x00, 0x03, 0x49, 0xc7, 0x5b,
+	0xb6
+};
+
+static const uint8_t attach_complete[] = {
+	0x0e, 0x00, 0x08,
+	0x01, 0xc0, 0x0d, 0x08, 0x03, 0x55, 0x1c, 0xea
+};
+
+static const uint8_t pdp_context[] = {
+	0x0e, 0x00, 0x5a,
+	0x01, 0xc0, 0x11, 0x0a, 0x41, 0x05, 0x03, 0x0c,
+	0x00, 0x00, 0x1f, 0x10, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x21, 0x28,
+	0x12, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+	0x65, 0x74, 0x05, 0x65, 0x70, 0x6c, 0x75, 0x73,
+	0x02, 0x64, 0x65, 0x27, 0x2a, 0x80, 0xc0, 0x23,
+	0x13, 0x01, 0x00, 0x00, 0x13, 0x05, 0x65, 0x70,
+	0x6c, 0x75, 0x73, 0x08, 0x69, 0x6e, 0x74, 0x65,
+	0x72, 0x6e, 0x65, 0x74, 0x80, 0x21, 0x10, 0x01,
+	0x00, 0x00, 0x10, 0x81, 0x06, 0x00, 0x00, 0x00,
+	0x00, 0x83, 0x06, 0x00, 0x00, 0x00, 0x00, 0xcf,
+	0x90, 0xcc
+};
+
+static const uint8_t qos_profile[] = { 0x0, 0x0, 0x04 };
+static uint32_t tlli = 0xadf11821;
+
+enum state {
+	Test_Start,
+	Test_IdRespIMEI,
+	Test_IdRespIMSI,
+	Test_AttachCompl,
+	Test_PDPAct,
+	Test_Done,
+};
+
+static enum state current_state = Test_Start;
+
+static void extract_tmsi_and_generate_tlli(struct msgb *msg, struct tlv_parsed *tp)
+{
+	uint32_t tmsi;
+	struct bssgp_ud_hdr *budh;
+	struct gprs_llc_hdr_parsed hp;
+	struct tlv_parsed ack_tp;
+	int rc;
+
+	gprs_llc_hdr_parse(&hp, TLVP_VAL(tp, BSSGP_IE_LLC_PDU),
+				TLVP_LEN(tp, BSSGP_IE_LLC_PDU));
+	msgb_gmmh(msg) = (unsigned char *) hp.data;
+
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
+
+	OSMO_ASSERT(gh->msg_type == GSM48_MT_GMM_ATTACH_ACK);
+	struct gsm48_attach_ack *ack = (struct gsm48_attach_ack *) gh->data;
+	rc = tlv_parse(&ack_tp, &gsm48_gmm_att_tlvdef, ack->data,
+			(msg->data + msg->len) - ack->data, 0, 0);
+
+
+	OSMO_ASSERT(TLVP_PRESENT(&ack_tp, GSM48_IE_GMM_ALLOC_PTMSI));	
+	memcpy(&tmsi, TLVP_VAL(&ack_tp, GSM48_IE_GMM_ALLOC_PTMSI) + 1, 4);
+	tmsi = ntohl(tmsi);
+	tlli = gprs_tmsi2tlli(tmsi, TLLI_LOCAL);
+	printf("New TLLI(0x%08x) based on tmsi(0x%x)\n", tlli, tmsi);
+}
+
+void test_pdp_activation_start(struct gprs_bssgp_pcu *pcu)
+{
+	struct msgb *msg = create_msg(attach, ARRAY_SIZE(attach));
+	bssgp_tx_ul_ud(pcu->bctx, tlli, qos_profile, msg);
+	current_state = Test_IdRespIMEI;
+}
+
+
+void test_pdp_activation_data(struct gprs_bssgp_pcu *pcu, struct msgb *msg, struct tlv_parsed *tp)
+{
+	const uint8_t *data;
+	size_t len;
+
+	switch (current_state) {
+	case Test_IdRespIMEI:
+		data = id_resp_imei;
+		len = ARRAY_SIZE(id_resp_imei);
+		current_state = Test_IdRespIMSI;
+		break;
+	case Test_IdRespIMSI:
+		data = id_resp_imsi;
+		len = ARRAY_SIZE(id_resp_imsi);
+		current_state = Test_AttachCompl;
+		break;
+	case Test_AttachCompl:
+		data = attach_complete;
+		len = ARRAY_SIZE(attach_complete);
+		extract_tmsi_and_generate_tlli(msg, tp);
+		current_state = Test_PDPAct;
+		break;
+	case Test_PDPAct:
+		printf("PDP context is active or not...\n");
+		return;
+		break;
+	case Test_Done:
+	case Test_Start: /* fall through */
+		return;
+		break;
+	default:
+		printf("Unknown state. %d\n", current_state);
+		return;
+		break;
+	};
+
+	struct msgb *out = create_msg(data, len);
+	bssgp_tx_ul_ud(pcu->bctx, tlli, qos_profile, out);
+
+	/* send it after the PDP... */
+	if (current_state == Test_PDPAct) {
+		out = create_msg(pdp_context, ARRAY_SIZE(pdp_context));
+		bssgp_tx_ul_ud(pcu->bctx, tlli, qos_profile, out);
+	}
+}
+
diff --git a/tests/emu/test_replay_gprs_attach.cpp b/tests/emu/test_replay_gprs_attach.cpp
index fb1e77f..9bcab57 100644
--- a/tests/emu/test_replay_gprs_attach.cpp
+++ b/tests/emu/test_replay_gprs_attach.cpp
@@ -24,6 +24,7 @@ extern "C" {
 }
 
 #include "openbsc_clone.h"
+#include "gprs_tests.h"
 
 #include <gprs_bssgp_pcu.h>
 
@@ -45,14 +46,6 @@ static const uint8_t gprs_attach_llc[] = {
 
 static int next_wanted_nu;
 
-struct msgb *create_msg(const uint8_t *data, size_t len)
-{
-	struct msgb *msg = msgb_alloc_headroom(4096, 128, "create msg");
-	msg->l3h = msgb_put(msg, len);
-	memcpy(msg->l3h, data, len);
-	return msg;
-}
-
 void test_replay_gprs_attach(struct gprs_bssgp_pcu *pcu)
 {
 	uint32_t tlli = 0xadf11820;
@@ -88,8 +81,8 @@ void test_replay_gprs_data(struct gprs_bssgp_pcu *pcu, struct msgb *msg, struct
 	OSMO_ASSERT(ph.seq_tx == next_wanted_nu++);
 
 	/* this test just wants to see messages... no further data is sent */
-	if (next_wanted_nu == 4) {
-		printf("Test done.\n");
-		exit(EXIT_SUCCESS);
+	if (next_wanted_nu == 6) {
+		printf("GPRS attach with increasing N(U) done.\n");
+		gprs_test_success(pcu);
 	}
 }
-- 
1.8.3.2





More information about the osmocom-net-gprs mailing list