[Spice-devel] [PATCH libcacard v2 19/35] simpletlv: Parse string to internal representation with tests
Jakub Jelen
jjelen at redhat.com
Thu Aug 2 09:43:51 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