[Spice-devel] [PATCH libcacard 29/45] simpletlv: Parse string to internal representation with tests

Jakub Jelen jjelen at redhat.com
Tue Jul 31 14:50:23 UTC 2018


 * The function parses SimpleTLV string to internal structures,
   that can be handled with more ease in the code.

Signed-off-by: Jakub Jelen <jjelen at redhat.com>
Reviewed-by: Robert Relyea <rrelyea at redhat.com>
---
 src/simpletlv.c   | 52 ++++++++++++++++++++++++++++++
 src/simpletlv.h   | 13 ++++++++
 tests/simpletlv.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 145 insertions(+)

diff --git a/src/simpletlv.c b/src/simpletlv.c
index 2a9a6a7..2f7618d 100644
--- a/src/simpletlv.c
+++ b/src/simpletlv.c
@@ -320,4 +320,56 @@ failure:
     free(new);
     return NULL;
 }
+
+struct simpletlv_member *
+simpletlv_parse(unsigned char *data, size_t data_len, size_t *outtlv_len)
+{
+    unsigned char *p, *p_end;
+    unsigned char tag;
+    size_t vlen, tlv_len = 0, tlv_allocated = 0;
+    struct simpletlv_member *tlv = NULL, *tlvp = NULL;
+
+    p = data;
+    p_end = p + data_len;
+    while (p < p_end) {
+        /* we can return what was parsed successfully */
+        if (simpletlv_read_tag(&p, p_end - p, &tag, &vlen) < 0) {
+            break;
+        }
+        if (vlen > (size_t) (p_end - p)) {
+            break;
+        }
+
+        /* Extend the allocated structure if needed */
+        if (tlv_len+1 > tlv_allocated) {
+            struct simpletlv_member *newtlv;
+            tlv_allocated += 10;
+            newtlv = realloc(tlv, tlv_allocated * sizeof(struct simpletlv_member));
+            if (newtlv == NULL) /* this is fatal */
+                goto failure;
+            tlv = newtlv;
+        }
+        tlvp = &(tlv[tlv_len++]);
+        tlvp->value.value = NULL;
+
+
+        tlvp->tag = tag;
+        tlvp->length = vlen;
+        tlvp->value.value = malloc(vlen);
+        if (tlvp->value.value == NULL) /* this is fatal */
+            goto failure;
+        memcpy(tlvp->value.value, p, vlen);
+        tlvp->type = SIMPLETLV_TYPE_LEAF;
+
+        p += vlen;
+    }
+
+    *outtlv_len = tlv_len;
+    return tlv;
+
+failure:
+    simpletlv_free(tlv, tlv_len);
+    return NULL;
+}
+
 /* vim: set ts=4 sw=4 tw=0 noet expandtab: */
diff --git a/src/simpletlv.h b/src/simpletlv.h
index e1cb5a2..23a86d5 100644
--- a/src/simpletlv.h
+++ b/src/simpletlv.h
@@ -124,4 +124,17 @@ simpletlv_read_tag(unsigned char **buf, size_t buflen,
 struct simpletlv_member *
 simpletlv_clone(struct simpletlv_member *tlv, size_t tlvlen);
 
+/* parse the SimpleTLV compound buffer into internal simpletlv structures
+ *
+ * The returned structure is NEVER recursive, since thre is no unambiguous
+ * way how to determine the recursive structures without the knowledge of
+ * a scheme in advance.
+ *
+ * The calling function is responsible for freeing the structure and its
+ * children by calling simpletlv_free().
+ *
+ */
+struct simpletlv_member *
+simpletlv_parse(unsigned char *data, size_t data_len, size_t *outtlv_len);
+
 #endif
diff --git a/tests/simpletlv.c b/tests/simpletlv.c
index 2be78db..cd0cd69 100644
--- a/tests/simpletlv.c
+++ b/tests/simpletlv.c
@@ -269,6 +269,84 @@ static void test_clone_simple(void)
     simpletlv_free(clone, 2);
 }
 
+static void test_parse_simple(void)
+{
+    unsigned char data[] = "\x13\x02\x14\x18\xDD\x03\x64\x24\x44";
+    size_t data_len = 9, tlv_len = 0;
+    struct simpletlv_member *tlv;
+
+    tlv = simpletlv_parse(data, data_len, &tlv_len);
+    g_assert_cmpint(tlv_len, ==, 2);
+
+    g_assert_cmpint(tlv[0].tag, ==, 0x13);
+    g_assert_cmpint(tlv[0].length, ==, 0x02);
+    g_assert_cmpint(tlv[0].type, ==, SIMPLETLV_TYPE_LEAF);
+    g_assert_cmpmem(tlv[0].value.value, tlv[0].length, "\x14\x18", 2);
+
+    g_assert_cmpint(tlv[1].tag, ==, 0xDD);
+    g_assert_cmpint(tlv[1].length, ==, 0x03);
+    g_assert_cmpint(tlv[1].type, ==, SIMPLETLV_TYPE_LEAF);
+    g_assert_cmpmem(tlv[1].value.value, tlv[1].length, "\x64\x24\x44", 3);
+
+    simpletlv_free(tlv, tlv_len);
+}
+
+/* If there is some gargabe in the end of buffer, we would like to
+ * return at least what was properly parsed, rahter than to fail
+ * hard.
+ * Also makes sure we do not leak memory or crash on bad data.
+ *
+ * This is an issue for OpenSC at this moment, which fails to encode
+ * last TLV into the compound buffer for data objects.
+ */
+static void test_parse_last_bad(void)
+{
+    size_t data_len = 9;
+    unsigned char data[] = "\x13\x02\x14\x18\x28\x13\x64\x24\x44";
+    /*                    this length is oveflow -^ */
+    size_t data2_len = 7;
+    unsigned char data2[] = "\x13\x02\x14\x18\x28\xFF\xFF";
+    /*           this length is missing last byte -^ */
+    size_t data3_len = 5;
+    unsigned char data3[] = "\x13\x02\x14\x18\x12";
+    /*         this length is missed completely -^ */
+    size_t tlv_len = 0;
+    struct simpletlv_member *tlv;
+
+    /* Test the overflow length in the last member */
+    tlv = simpletlv_parse(data, data_len, &tlv_len);
+    g_assert_cmpint(tlv_len, ==, 1);
+
+    g_assert_cmpint(tlv[0].tag, ==, 0x13);
+    g_assert_cmpint(tlv[0].length, ==, 0x02);
+    g_assert_cmpint(tlv[0].type, ==, SIMPLETLV_TYPE_LEAF);
+    g_assert_cmpmem(tlv[0].value.value, tlv[0].length, "\x14\x18", 2);
+
+    simpletlv_free(tlv, tlv_len);
+
+    /* Test the truncated length item in last member */
+    tlv = simpletlv_parse(data2, data2_len, &tlv_len);
+    g_assert_cmpint(tlv_len, ==, 1);
+
+    g_assert_cmpint(tlv[0].tag, ==, 0x13);
+    g_assert_cmpint(tlv[0].length, ==, 0x02);
+    g_assert_cmpint(tlv[0].type, ==, SIMPLETLV_TYPE_LEAF);
+    g_assert_cmpmem(tlv[0].value.value, tlv[0].length, "\x14\x18", 2);
+
+    simpletlv_free(tlv, tlv_len);
+
+    /* Test the missing length item in last member */
+    tlv = simpletlv_parse(data3, data3_len, &tlv_len);
+    g_assert_cmpint(tlv_len, ==, 1);
+
+    g_assert_cmpint(tlv[0].tag, ==, 0x13);
+    g_assert_cmpint(tlv[0].length, ==, 0x02);
+    g_assert_cmpint(tlv[0].type, ==, SIMPLETLV_TYPE_LEAF);
+    g_assert_cmpmem(tlv[0].value.value, tlv[0].length, "\x14\x18", 2);
+
+    simpletlv_free(tlv, tlv_len);
+}
+
 int main(int argc, char *argv[])
 {
     int ret;
@@ -283,6 +361,8 @@ int main(int argc, char *argv[])
     g_test_add_func("/simpletlv/encode/simple", test_encode_simple);
     g_test_add_func("/simpletlv/encode/nested", test_encode_nested);
     g_test_add_func("/simpletlv/encode/skipped", test_encode_skipped);
+    g_test_add_func("/simpletlv/parse/simple", test_parse_simple);
+    g_test_add_func("/simpletlv/parse/last_bad", test_parse_last_bad);
     g_test_add_func("/simpletlv/clone/simple", test_clone_simple);
 
     ret = g_test_run();
-- 
2.17.1



More information about the Spice-devel mailing list