[PATCH] Improve hex parser robustness.

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/baseband-devel@lists.osmocom.org/.

Max Max.Suraev at fairwaves.ru
Mon Jan 14 17:14:30 UTC 2013


---
 .gitignore                   |    1 +
 include/osmocom/core/utils.h |    5 ++++
 src/utils.c                  |   68 ++++++++++++++++++++++++++++++++----------
 tests/Makefile.am            |    7 +++--
 tests/hex/hex_test.c         |   52 ++++++++++++++++++++++++++++++++
 tests/hex/hex_test.ok        |   18 +++++++++++
 tests/testsuite.at           |    6 ++++
 7 files changed, 140 insertions(+), 17 deletions(-)
 create mode 100644 tests/hex/hex_test.c
 create mode 100644 tests/hex/hex_test.ok

diff --git a/.gitignore b/.gitignore
index ac19b23..67d9196 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,6 +58,7 @@ tests/sms/sms_test
 tests/timer/timer_test
 tests/msgfile/msgfile_test
 tests/ussd/ussd_test
+tests/hex/hex_test
 tests/smscb/smscb_test
 tests/bits/bitrev_test
 tests/a5/a5_test
diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h
index 03861d7..5250094 100644
--- a/include/osmocom/core/utils.h
+++ b/include/osmocom/core/utils.h
@@ -30,6 +30,11 @@ char osmo_bcd2char(uint8_t bcd);
 /* only works for numbers in ascci */
 uint8_t osmo_char2bcd(char c);
 
+enum hex_len_policy { /* define how to deal with odd length during hex string parsing */
+    HEX_ODD_NONE, HEX_ODD_LEFT, HEX_ODD_RIGHT
+};
+
+int osmo_hexparse_ext(const char *str, uint8_t *b, int max_len, enum hex_len_policy pol);
 int osmo_hexparse(const char *str, uint8_t *b, int max_len);
 
 char *osmo_ubit_dump(const uint8_t *bits, unsigned int len);
diff --git a/src/utils.c b/src/utils.c
index c36979c..cd11618 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -73,36 +73,74 @@ uint8_t osmo_char2bcd(char c)
 	return c - 0x30;
 }
 
-/*! \brief Parse a string ocntaining hexadecimal digits
+/*! \brief Parse a string ocntaining hexadecimal digits, optionally prefixed with 0x
  *  \param[in] str string containing ASCII encoded hexadecimal digits
  *  \param[out] b output buffer
  *  \param[in] max_len maximum space in output buffer
  */
 int osmo_hexparse(const char *str, uint8_t *b, int max_len)
+{
+    return osmo_hexparse_ext(str, b, max_len, HEX_ODD_NONE);
+}
 
+inline int _osmo_hexval(char c)
 {
-	int i, l, v;
+    if (c >= '0' && c <= '9')
+	return c - '0';
+    if (c >= 'a' && c <= 'f')
+	return 10 + (c - 'a');
+    if (c >= 'A' && c <= 'F')
+	return 10 + (c - 'A');
+    return -1;
+}
 
-	l = strlen(str);
-	if ((l&1) || ((l>>1) > max_len))
+/*! \brief Parse a string ocntaining hexadecimal digits, optionally prefixed with 0x
+ *  \param[in] str string containing ASCII encoded hexadecimal digits
+ *  \param[out] b output buffer
+ *  \param[in] max_len maximum space in output buffer
+ *  \param[in] pol defines how to treat odd length case
+ */
+int osmo_hexparse_ext(const char *str, uint8_t *b, int max_len, enum hex_len_policy pol)
+{
+	int v;
+        size_t i, left_adj = 0, right_adj = 0, l = strlen(str);
+	const char * src;
+
+	if (l > 2) { /* remove 0x prefix if any */
+	    if (!strncmp(str, "0x", 2)) {
+		l -= 2;
+		str += 2;
+	    }
+	}
+
+        if (l & 1) {
+            switch (pol) {
+                case HEX_ODD_LEFT:
+                    left_adj = 1; 
+                break;
+                case HEX_ODD_NONE:
+                    return -1;
+                case HEX_ODD_RIGHT:
+                    right_adj = 1;
+                break;
+                default:
+                    return -1;
+            }
+	}
+        /* left_adj and right_adj are mutually exclusive so left_adj + right_adj is either 1 or 0 */
+	if ((l>>1) > max_len + left_adj + right_adj)
 		return -1;
 
-	memset(b, 0x00, max_len);
+	memset(b, 0x00, max_len + left_adj + right_adj);
 
 	for (i=0; i<l; i++) {
-		char c = str[i];
-		if (c >= '0' && c <= '9')
-			v = c - '0';
-		else if (c >= 'a' && c <= 'f')
-			v = 10 + (c - 'a');
-		else if (c >= 'A' && c <= 'F')
-			v = 10 + (c - 'A');
-		else
+                v = _osmo_hexval(str[i]);
+		if (-1 == v)
 			return -1;
-		b[i>>1] |= v << (i&1 ? 0 : 4);
+                b[(i + left_adj) >> 1] |= v << ( (i + left_adj) & 1 ? 0 : 4);
 	}
 
-	return i>>1;
+        return (l + left_adj + right_adj)/2;
 }
 
 static char hexd_buff[4096];
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b60f675..fbefab1 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -4,7 +4,7 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test		\
                  smscb/smscb_test bits/bitrev_test a5/a5_test		\
                  conv/conv_test auth/milenage_test lapd/lapd_test	\
                  gsm0808/gsm0808_test gsm0408/gsm0408_test		\
-		 gb/bssgp_fc_test logging/logging_test
+		 gb/bssgp_fc_test hex/hex_test logging/logging_test
 if ENABLE_MSGFILE
 check_PROGRAMS += msgfile/msgfile_test
 endif
@@ -21,6 +21,9 @@ bits_bitrev_test_LDADD = $(top_builddir)/src/libosmocore.la
 conv_conv_test_SOURCES = conv/conv_test.c
 conv_conv_test_LDADD = $(top_builddir)/src/libosmocore.la
 
+hex_hex_test_SOURCES = hex/hex_test.c
+hex_hex_test_LDADD = $(top_builddir)/src/libosmocore.la
+
 gsm0808_gsm0808_test_SOURCES = gsm0808/gsm0808_test.c
 gsm0808_gsm0808_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
 
@@ -77,7 +80,7 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE)		\
              gsm0808/gsm0808_test.ok gb/bssgp_fc_tests.err		\
              gb/bssgp_fc_tests.ok gb/bssgp_fc_tests.sh			\
              msgfile/msgfile_test.ok msgfile/msgconfig.cfg		\
-             logging/logging_test.ok logging/logging_test.err
+             logging/logging_test.ok hex/hex_test.ok logging/logging_test.err
 
 DISTCLEANFILES = atconfig
 
diff --git a/tests/hex/hex_test.c b/tests/hex/hex_test.c
new file mode 100644
index 0000000..fa85065
--- /dev/null
+++ b/tests/hex/hex_test.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <osmocom/core/utils.h>
+
+/* ------------------------------------------------------------------------ */
+/* Test vectors                                                             */
+/* ------------------------------------------------------------------------ */
+
+char * long_rand_x = "0xa63c3b465eb79b3e06ae66eb816eace";
+char * long_rand = "39ea2e536e6a2c0d3e59110df20e4bf";
+char * small_even_x = "0xDEAD";
+char * small_even = "FACE";
+char * small_odd_x = "0xFFACE";
+char * small_odd = "bee";
+
+/* ------------------------------------------------------------------------ */
+/* Main                                                                     */
+/* ------------------------------------------------------------------------ */
+
+inline void _run_tests(char * vec, size_t len)
+{
+    uint8_t test[len];
+
+    int parsed = osmo_hexparse_ext(vec, test, len, HEX_ODD_NONE);
+    printf("NONE parsed %s: %d", vec, parsed);
+    if (-1 != parsed) printf(", dumped: %s\n", osmo_hexdump_nospc(test, len));
+    else printf("\n");
+
+    parsed = osmo_hexparse_ext(vec, test, len, HEX_ODD_LEFT);
+    printf("LEFT parsed %s: %d", vec, parsed);
+    if (-1 != parsed) printf(", dumped: %s\n", osmo_hexdump_nospc(test, len));
+    else printf("\n");
+
+    parsed = osmo_hexparse_ext(vec, test, len, HEX_ODD_RIGHT);
+    printf("RIGHT parsed %s: %d", vec, parsed);
+    if (-1 != parsed) printf(", dumped: %s\n", osmo_hexdump_nospc(test, len));
+    else printf("\n");
+}
+
+int main(int argc, char argv[])
+{
+    _run_tests(long_rand_x, 16);
+    _run_tests(long_rand, 16);
+    _run_tests(small_even_x, 2);
+    _run_tests(small_even, 2);
+    _run_tests(small_odd_x, 3);
+    _run_tests(small_odd, 2);
+
+    return 0;
+}
diff --git a/tests/hex/hex_test.ok b/tests/hex/hex_test.ok
new file mode 100644
index 0000000..1419b8c
--- /dev/null
+++ b/tests/hex/hex_test.ok
@@ -0,0 +1,18 @@
+NONE parsed 0xa63c3b465eb79b3e06ae66eb816eace: -1
+LEFT parsed 0xa63c3b465eb79b3e06ae66eb816eace: 16, dumped: 0a63c3b465eb79b3e06ae66eb816eace
+RIGHT parsed 0xa63c3b465eb79b3e06ae66eb816eace: 16, dumped: a63c3b465eb79b3e06ae66eb816eace0
+NONE parsed 39ea2e536e6a2c0d3e59110df20e4bf: -1
+LEFT parsed 39ea2e536e6a2c0d3e59110df20e4bf: 16, dumped: 039ea2e536e6a2c0d3e59110df20e4bf
+RIGHT parsed 39ea2e536e6a2c0d3e59110df20e4bf: 16, dumped: 39ea2e536e6a2c0d3e59110df20e4bf0
+NONE parsed 0xDEAD: 2, dumped: dead
+LEFT parsed 0xDEAD: 2, dumped: dead
+RIGHT parsed 0xDEAD: 2, dumped: dead
+NONE parsed FACE: 2, dumped: face
+LEFT parsed FACE: 2, dumped: face
+RIGHT parsed FACE: 2, dumped: face
+NONE parsed 0xFFACE: -1
+LEFT parsed 0xFFACE: 3, dumped: 0fface
+RIGHT parsed 0xFFACE: 3, dumped: fface0
+NONE parsed bee: -1
+LEFT parsed bee: 2, dumped: 0bee
+RIGHT parsed bee: 2, dumped: bee0
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 1cfae03..cfbcf78 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -28,6 +28,12 @@ cat $abs_srcdir/conv/conv_test.ok > expout
 AT_CHECK([$abs_top_builddir/tests/conv/conv_test], [], [expout])
 AT_CLEANUP
 
+AT_SETUP([hex])
+AT_KEYWORDS([hex])
+cat $abs_srcdir/hex/hex_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/hex/hex_test], [], [expout])
+AT_CLEANUP
+
 if ENABLE_MSGFILE
 AT_SETUP([msgfile])
 AT_KEYWORDS([msgfile])
-- 
1.7.10.4


--------------040406000709000102010704--




More information about the baseband-devel mailing list