Neels Hofmeyr has uploaded this change for review. ( 
https://gerrit.osmocom.org/12151


Change subject: gsm0408_test: test encoding and decoding Mobile Identity
......................................................................

gsm0408_test: test encoding and decoding Mobile Identity

One would think by now we would solidly encode and decode Mobile Identities.
Well, guess again.

- rc is sometimes the amount of bytes written, sometimes actual strlen().

- on string truncation, rc is sometimes strlen() (assuming truncation), and
  sometimes snprintf()-style would-be strlen().

- returned string, when truncated by not enough buffer size, is sometimes nul
  terminated, sometimes not.

- gsm48_mi_to_string() happily reads a byte from zero-length input buffer.

- gsm48_mi_to_string() happily writes to zero length output buffer.

- gsm48_mi_to_string() returns nonempty string for empty input.

- encoding a MI type that still has the GSM_MI_ODD flag set results in encoding
  an even-length MI as odd-length (hence appending a stray 'F').

I am going to tweak the implementation of gsm48 mobile identity encoding /
decoding, so first pinpoint the current behavior in a unit test, and show how
perforated even such a seemingly trivial API can be.

Change-Id: Iaae3af87f82f1a8f2e6273984c011b2813038cf7
---
M tests/gsm0408/gsm0408_test.c
M tests/gsm0408/gsm0408_test.ok
2 files changed, 358 insertions(+), 0 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/51/12151/1

diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c
index 2a0e661..d38969b 100644
--- a/tests/gsm0408/gsm0408_test.c
+++ b/tests/gsm0408/gsm0408_test.c
@@ -352,11 +352,246 @@
        printf("passed: [%u] %s\n", len, osmo_hexdump(buf, len));
 }

+struct test_mid_encode_decode_test {
+       uint8_t mi_type;
+       const char *mi_str;
+       size_t str_size;
+       const char *expect_mi_tlv_hex;
+       const char *expect_str;
+       int expect_rc;
+};
+
+static const struct test_mid_encode_decode_test test_mid_encode_decode_tests[] 
= {
+       {
+               .mi_type = GSM_MI_TYPE_IMSI,
+               .mi_str = "123456789012345",
+               .expect_mi_tlv_hex = "17081932547698103254",
+       },
+       {
+               .mi_type = GSM_MI_TYPE_IMSI,
+               .mi_str = "12345678901234",
+               .expect_mi_tlv_hex = "170811325476981032f4",
+       },
+       {
+               .mi_type = GSM_MI_TYPE_IMSI,
+               .mi_str = "423423",
+               .expect_mi_tlv_hex = "1704413224f3",
+       },
+       {
+               .mi_type = GSM_MI_TYPE_IMSI | GSM_MI_ODD,
+               .mi_str = "423423",
+               .expect_mi_tlv_hex = "1704493224f3", /* encodes "odd" for even 
number of digits! */
+       },
+       {
+               .mi_type = GSM_MI_TYPE_IMSI,
+               .mi_str = "4234235",
+               .expect_mi_tlv_hex = "170449322453",
+       },
+       {
+               .mi_type = GSM_MI_TYPE_IMSI,
+               .mi_str = "4234235",
+               .expect_mi_tlv_hex = "170449322453",
+               .str_size = 4,
+               .expect_str = "423",
+               .expect_rc = 3, /* exception: on truncation, 
gsm48_mi_to_string() returns strlen(), not bytes! */
+       },
+       {
+               .mi_type = GSM_MI_TYPE_IMEI,
+               .mi_str = "123456789012345",
+               .expect_mi_tlv_hex = "17081a32547698103254",
+       },
+       {
+               .mi_type = GSM_MI_TYPE_IMEI,
+               .mi_str = "98765432109876",
+               .expect_mi_tlv_hex = "170892785634129078f6",
+       },
+       {
+               .mi_type = GSM_MI_TYPE_IMEI,
+               .mi_str = "987654321098765",
+               .expect_mi_tlv_hex = "17089a78563412907856",
+       },
+       {
+               .mi_type = GSM_MI_TYPE_IMEISV,
+               .mi_str = "987654321098765432",
+               .expect_mi_tlv_hex = "170a937856341290785634f2",
+       },
+       {
+               .mi_type = GSM_MI_TYPE_IMEISV,
+               .mi_str = "987654321098765432",
+               .expect_mi_tlv_hex = "170a937856341290785634f2",
+               .str_size = 16,
+               .expect_str = "987654321098765",
+               .expect_rc = 15, /* exception: on truncation, 
gsm48_mi_to_string() returns strlen(), not bytes! */
+       },
+       {
+               /* gsm48 treats TMSI as decimal string */
+               .mi_type = GSM_MI_TYPE_TMSI,
+               .mi_str = "305419896", /* 0x12345678 as decimal */
+               .expect_mi_tlv_hex = "1705f412345678",
+               .expect_rc = 9, /* exception: gsm48_mi_to_string() for TMSI 
returns strlen(), not bytes! */
+       },
+       {
+               .mi_type = GSM_MI_TYPE_TMSI,
+               .mi_str = "12648430", /* 0xc0ffee as decimal */
+               .expect_mi_tlv_hex = "1705f400c0ffee",
+               .expect_rc = 8, /* exception: gsm48_mi_to_string() for TMSI 
returns strlen(), not bytes! */
+       },
+       {
+               .mi_type = GSM_MI_TYPE_TMSI,
+               .mi_str = "0",
+               .expect_mi_tlv_hex = "1705f400000000",
+               .expect_rc = 1, /* exception: gsm48_mi_to_string() for TMSI 
returns strlen(), not bytes! */
+       },
+       {
+               /* gsm48 treats TMSI as decimal string */
+               .mi_type = GSM_MI_TYPE_TMSI,
+               .mi_str = "305419896", /* 0x12345678 as decimal */
+               .expect_mi_tlv_hex = "1705f412345678",
+               .str_size = 5,
+               .expect_str = "3054",
+               .expect_rc = 9, /* exception: gsm48_mi_to_string() for TMSI 
returns would-be strlen() like snprintf()! */
+       },
+       {
+               .mi_type = GSM_MI_TYPE_NONE,
+               .mi_str = "123",
+               .expect_mi_tlv_hex = "17021832", /* encoding nonsense! */
+               .expect_str = "",
+       },
+       {
+               .mi_type = GSM_MI_TYPE_NONE,
+               .mi_str = "1234",
+               .expect_mi_tlv_hex = "17031032f4", /* encoding nonsense! */
+               .expect_str = "",
+       },
+       {
+               .mi_type = GSM_MI_ODD,
+               .mi_str = "1234",
+               .expect_mi_tlv_hex = "17031832f4", /* encoding nonsense and 
encodes "odd" for an even number of digits! */
+               .expect_str = "",
+       },
+};
+
+static void test_mid_encode_decode(void)
+{
+       int i;
+
+       printf("\nTesting Mobile Identity conversions\n");
+
+       for (i = 0; i < ARRAY_SIZE(test_mid_encode_decode_tests); i++) {
+               const struct test_mid_encode_decode_test *t = 
&test_mid_encode_decode_tests[i];
+               uint8_t tlv_buf[64];
+               uint8_t *mi_buf;
+               int tlv_len;
+               int mi_len;
+               const char *tlv_hex;
+               char str[64] = {};
+               size_t str_size = t->str_size ? : sizeof(str);
+               const char *expect_str = t->expect_str ? : t->mi_str;
+               int expect_rc = t->expect_rc ? : strlen(expect_str)+1;
+               int rc;
+               int str_len;
+
+               printf("- %s %s\n", gsm48_mi_type_name(t->mi_type), t->mi_str);
+               if (t->mi_type == GSM_MI_TYPE_TMSI)
+                       tlv_len = gsm48_generate_mid_from_tmsi(tlv_buf, 
(uint32_t)atoll(t->mi_str));
+               else
+                       tlv_len = gsm48_generate_mid(tlv_buf, t->mi_str, 
t->mi_type);
+               tlv_hex = osmo_hexdump_nospc(tlv_buf, tlv_len);
+
+               printf("  -> MI-TLV-hex='%s'\n", tlv_hex);
+               if (t->expect_mi_tlv_hex && strcmp(tlv_hex, 
t->expect_mi_tlv_hex)) {
+                       printf("     ERROR: expected '%s'\n", 
t->expect_mi_tlv_hex);
+               }
+
+               /* skip the GSM48_IE_MOBILE_ID tag and length */
+               mi_buf = tlv_buf + 2;
+               mi_len = tlv_len - 2;
+
+               rc = gsm48_mi_to_string(str, str_size, mi_buf, mi_len);
+               printf("  -> MI-str=%s rc=%d\n", osmo_quote_str(str, -1), rc);
+               if (strcmp(str, expect_str))
+                       printf("     ERROR: expected MI-str=%s\n", 
osmo_quote_str(expect_str, -1));
+               if (rc != expect_rc)
+                       printf("     ERROR: expected rc=%d\n", expect_rc);
+
+               /* Now make sure the resulting string is always '\0' terminated.
+                * The above started out with a zeroed buffer, now repeat with 
a tainted one. */
+               str_len = strlen(str);
+               str[str_len] = '!';
+               gsm48_mi_to_string(str, str_size, mi_buf, mi_len);
+               if (strlen(str) != str_len)
+                       printf("     ERROR: resulting string is not explicitly 
nul terminated\n");
+       }
+}
+
+static const uint8_t test_mid_decode_zero_length_types[] = { GSM_MI_TYPE_IMSI, 
GSM_MI_TYPE_TMSI, GSM_MI_TYPE_NONE };
+
+static void test_mid_decode_zero_length(void)
+{
+       int odd;
+       uint8_t valid_mi[64];
+       int valid_mi_len;
+
+       printf("\nDecoding zero length Mobile Identities\n");
+
+       /* IMSI = 123456789012345 */
+       valid_mi_len = osmo_hexparse("1932547698103254", valid_mi, 
sizeof(valid_mi));
+
+       for (odd = 0; odd <= 1; odd++) {
+               int i;
+               for (i = 0; i < ARRAY_SIZE(test_mid_decode_zero_length_types); 
i++) {
+                       uint8_t mi_type = test_mid_decode_zero_length_types[i] 
| (odd ? GSM_MI_ODD : 0);
+                       char str[8] = {};
+                       int rc;
+
+                       printf("- MI type: %s%s\n", gsm48_mi_type_name(mi_type 
& GSM_MI_TYPE_MASK),
+                              odd ? " | GSM_MI_ODD":"");
+                       valid_mi[0] = (valid_mi[0] & 0xf0) | mi_type;
+
+                       printf("  - writing to zero-length string:\n");
+                       memset(str, '!', sizeof(str) - 1);
+                       rc = gsm48_mi_to_string(str, 0, valid_mi, valid_mi_len);
+                       printf("    rc=%d\n", rc);
+                       if (str[0] == '!')
+                               printf("    nothing written\n");
+                       else
+                               printf("    ERROR: Wrote to invalid memory!\n");
+
+                       printf("  - writing to 1-byte-length string:\n");
+                       memset(str, '!', sizeof(str) - 1);
+                       rc = gsm48_mi_to_string(str, 1, valid_mi, valid_mi_len);
+                       printf("    rc=%d\n", rc);
+                       if (str[0] == '\0')
+                               printf("    returned empty string\n");
+                       else if (str[0] == '!')
+                               printf("    ERROR: nothing written, expected 
nul-terminated empty string\n");
+                       else
+                               printf("    ERROR: Wrote unexpected string 
%s\n", osmo_quote_str(str, 5));
+                       if (str[1] != '!')
+                               printf("    ERROR: Wrote to invalid memory!\n");
+
+                       printf("  - decode zero-length mi:\n");
+                       memset(str, '!', sizeof(str) - 1);
+                       rc = gsm48_mi_to_string(str, sizeof(str), valid_mi, 0);
+                       printf("    rc=%d\n", rc);
+                       if (str[0] == '\0')
+                               printf("    returned empty string\n");
+                       else if (str[0] == '!')
+                               printf("    ERROR: nothing written, expected 
nul-terminated empty string\n");
+                       else
+                               printf("    ERROR: expected empty string, got 
output string: %s\n", osmo_quote_str(str, -1));
+               }
+       }
+       printf("\n");
+}
+
 int main(int argc, char **argv)
 {
        test_bearer_cap();
        test_mid_from_tmsi();
        test_mid_from_imsi();
+       test_mid_encode_decode();
+       test_mid_decode_zero_length();
        test_ra_cap();
        test_lai_encode_decode();

diff --git a/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok
index c1d6a70..1dc4249 100644
--- a/tests/gsm0408/gsm0408_test.ok
+++ b/tests/gsm0408/gsm0408_test.ok
@@ -2,6 +2,129 @@
 Test `Speech, all codecs' passed
 Simple TMSI encoding test....passed
 Simple IMSI encoding test....passed: [10] 17 08 99 10 07 00 00 00 64 02
+
+Testing Mobile Identity conversions
+- IMSI 123456789012345
+  -> MI-TLV-hex='17081932547698103254'
+  -> MI-str="123456789012345" rc=16
+- IMSI 12345678901234
+  -> MI-TLV-hex='170811325476981032f4'
+  -> MI-str="12345678901234" rc=15
+- IMSI 423423
+  -> MI-TLV-hex='1704413224f3'
+  -> MI-str="423423" rc=7
+- unknown 0x9 423423
+  -> MI-TLV-hex='1704493224f3'
+  -> MI-str="423423F" rc=8
+     ERROR: expected MI-str="423423"
+     ERROR: expected rc=7
+- IMSI 4234235
+  -> MI-TLV-hex='170449322453'
+  -> MI-str="4234235" rc=8
+- IMSI 4234235
+  -> MI-TLV-hex='170449322453'
+  -> MI-str="423" rc=3
+     ERROR: resulting string is not explicitly nul terminated
+- IMEI 123456789012345
+  -> MI-TLV-hex='17081a32547698103254'
+  -> MI-str="123456789012345" rc=16
+- IMEI 98765432109876
+  -> MI-TLV-hex='170892785634129078f6'
+  -> MI-str="98765432109876" rc=15
+- IMEI 987654321098765
+  -> MI-TLV-hex='17089a78563412907856'
+  -> MI-str="987654321098765" rc=16
+- IMEI-SV 987654321098765432
+  -> MI-TLV-hex='170a937856341290785634f2'
+  -> MI-str="987654321098765432" rc=19
+- IMEI-SV 987654321098765432
+  -> MI-TLV-hex='170a937856341290785634f2'
+  -> MI-str="987654321098765" rc=15
+     ERROR: resulting string is not explicitly nul terminated
+- TMSI 305419896
+  -> MI-TLV-hex='1705f412345678'
+  -> MI-str="305419896" rc=9
+- TMSI 12648430
+  -> MI-TLV-hex='1705f400c0ffee'
+  -> MI-str="12648430" rc=8
+- TMSI 0
+  -> MI-TLV-hex='1705f400000000'
+  -> MI-str="0" rc=1
+- TMSI 305419896
+  -> MI-TLV-hex='1705f412345678'
+  -> MI-str="3054" rc=9
+- NONE 123
+  -> MI-TLV-hex='17021832'
+  -> MI-str="" rc=1
+- NONE 1234
+  -> MI-TLV-hex='17031032f4'
+  -> MI-str="" rc=1
+- unknown 0x8 1234
+  -> MI-TLV-hex='17031832f4'
+  -> MI-str="" rc=1
+
+Decoding zero length Mobile Identities
+- MI type: IMSI
+  - writing to zero-length string:
+    rc=1
+    ERROR: Wrote to invalid memory!
+  - writing to 1-byte-length string:
+    rc=1
+    ERROR: Wrote unexpected string "1!!!!"
+  - decode zero-length mi:
+    rc=2
+    ERROR: expected empty string, got output string: "1"
+- MI type: TMSI
+  - writing to zero-length string:
+    rc=1
+    ERROR: Wrote to invalid memory!
+  - writing to 1-byte-length string:
+    rc=1
+    returned empty string
+  - decode zero-length mi:
+    rc=1
+    returned empty string
+- MI type: NONE
+  - writing to zero-length string:
+    rc=1
+    ERROR: Wrote to invalid memory!
+  - writing to 1-byte-length string:
+    rc=1
+    returned empty string
+  - decode zero-length mi:
+    rc=1
+    returned empty string
+- MI type: IMSI | GSM_MI_ODD
+  - writing to zero-length string:
+    rc=1
+    ERROR: Wrote to invalid memory!
+  - writing to 1-byte-length string:
+    rc=1
+    ERROR: Wrote unexpected string "1!!!!"
+  - decode zero-length mi:
+    rc=2
+    ERROR: expected empty string, got output string: "1"
+- MI type: TMSI | GSM_MI_ODD
+  - writing to zero-length string:
+    rc=1
+    ERROR: Wrote to invalid memory!
+  - writing to 1-byte-length string:
+    rc=1
+    returned empty string
+  - decode zero-length mi:
+    rc=1
+    returned empty string
+- MI type: NONE | GSM_MI_ODD
+  - writing to zero-length string:
+    rc=1
+    ERROR: Wrote to invalid memory!
+  - writing to 1-byte-length string:
+    rc=1
+    returned empty string
+  - decode zero-length mi:
+    rc=1
+    returned empty string
+
 Constructed RA:
 077-121-666-5
 MCC+MNC in BCD: 70 17 21

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

Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: Iaae3af87f82f1a8f2e6273984c011b2813038cf7
Gerrit-Change-Number: 12151
Gerrit-PatchSet: 1
Gerrit-Owner: Neels Hofmeyr <[email protected]>

Reply via email to