[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         |   57 +++++++++++++++++++++++++++++++++++
 tests/hex/hex_test.ok        |   18 +++++++++++
 tests/testsuite.at           |    6 ++++
 7 files changed, 145 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..a93d72c 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);
+}
 
+static 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) + left_adj + right_adj > max_len)
 		return -1;
 
 	memset(b, 0x00, max_len);
 
-	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
+	for (i = 0; i < l; i++) {
+		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..5c060c4
--- /dev/null
+++ b/tests/hex/hex_test.c
@@ -0,0 +1,57 @@
+#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 _single_test(char * vec, size_t len, uint8_t * test, enum hex_len_policy pol)
+{
+    int r = osmo_hexparse_ext(vec, test, len, pol);
+    printf(" parsed %s (%u): %d", vec, len, r);
+    if (-1 != r) printf(", dumped: %s, check %x,%d,%d\n", osmo_hexdump_nospc(test, len), test[len - 1], test[len], test[len + 1]);
+    else printf("\n");
+}
+
+inline void _run_tests(char * vec, size_t len)
+{
+    uint8_t test[len + 4];
+    int r;
+
+    for (r = 0; r < len + 4; r++) test[r] = r;
+
+    printf("NONE");
+    _single_test(vec, len, test, HEX_ODD_NONE);
+
+    printf("LEFT");
+    _single_test(vec, len, test, HEX_ODD_LEFT);
+    
+    printf("RIGHT");
+    _single_test(vec, len, test, HEX_ODD_RIGHT);
+}
+
+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..44e89d2
--- /dev/null
+++ b/tests/hex/hex_test.ok
@@ -0,0 +1,18 @@
+NONE parsed 0xa63c3b465eb79b3e06ae66eb816eace (16): -1
+LEFT parsed 0xa63c3b465eb79b3e06ae66eb816eace (16): 16, dumped: 0a63c3b465eb79b3e06ae66eb816eace, check ce,16,17
+RIGHT parsed 0xa63c3b465eb79b3e06ae66eb816eace (16): 16, dumped: a63c3b465eb79b3e06ae66eb816eace0, check e0,16,17
+NONE parsed 39ea2e536e6a2c0d3e59110df20e4bf (16): -1
+LEFT parsed 39ea2e536e6a2c0d3e59110df20e4bf (16): 16, dumped: 039ea2e536e6a2c0d3e59110df20e4bf, check bf,16,17
+RIGHT parsed 39ea2e536e6a2c0d3e59110df20e4bf (16): 16, dumped: 39ea2e536e6a2c0d3e59110df20e4bf0, check f0,16,17
+NONE parsed 0xDEAD (2): 2, dumped: dead, check ad,2,3
+LEFT parsed 0xDEAD (2): 2, dumped: dead, check ad,2,3
+RIGHT parsed 0xDEAD (2): 2, dumped: dead, check ad,2,3
+NONE parsed FACE (2): 2, dumped: face, check ce,2,3
+LEFT parsed FACE (2): 2, dumped: face, check ce,2,3
+RIGHT parsed FACE (2): 2, dumped: face, check ce,2,3
+NONE parsed 0xFFACE (3): -1
+LEFT parsed 0xFFACE (3): 3, dumped: 0fface, check ce,3,4
+RIGHT parsed 0xFFACE (3): 3, dumped: fface0, check e0,3,4
+NONE parsed bee (2): -1
+LEFT parsed bee (2): 2, dumped: 0bee, check ee,2,3
+RIGHT parsed bee (2): 2, dumped: bee0, check e0,2,3
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


--------------080702070805090503080203--




More information about the baseband-devel mailing list