* The function parses SimpleTLV string to internal structures, that can be handled with more ease in the code. Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx> Reviewed-by: Robert Relyea <rrelyea@xxxxxxxxxx> --- 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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel