-
Notifications
You must be signed in to change notification settings - Fork 3
Add indefinite lenght support to cbor read functions. #111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -41,11 +41,33 @@ | |||||
| #include <nat20/types.h> | ||||||
|
|
||||||
| void n20_cbor_write_header(n20_stream_t *const s, n20_cbor_type_t cbor_type, uint64_t n) { | ||||||
| if ((unsigned int)cbor_type > 7) { | ||||||
| /* 0xf7 is the encoding of the special value "undefined". */ | ||||||
| cbor_type = n20_cbor_type_simple_float_e; | ||||||
| n = N20_SIMPLE_UNDEFINED; | ||||||
| switch (cbor_type) { | ||||||
| case n20_cbor_type_uint_e: | ||||||
| case n20_cbor_type_nint_e: | ||||||
| case n20_cbor_type_bytes_e: | ||||||
| case n20_cbor_type_string_e: | ||||||
| case n20_cbor_type_array_e: | ||||||
| case n20_cbor_type_map_e: | ||||||
| case n20_cbor_type_tag_e: | ||||||
| case n20_cbor_type_simple_float_e: | ||||||
| break; | ||||||
| case n20_cbor_type_indefinite_bytes_e: | ||||||
| case n20_cbor_type_indefinite_string_e: | ||||||
| case n20_cbor_type_indefinite_array_e: | ||||||
| case n20_cbor_type_indefinite_map_e: | ||||||
| case n20_cbor_type_indefinite_break_e: { | ||||||
| cbor_type = (n20_cbor_type_t)(cbor_type - 0x100); | ||||||
| uint8_t header = (uint8_t)(cbor_type << 5) | 31; | ||||||
| n20_stream_prepend(s, &header, /*src_len=*/1); | ||||||
| return; | ||||||
| } | ||||||
| default: | ||||||
| /* Invalid types are encoded as "undefined". */ | ||||||
| cbor_type = n20_cbor_type_simple_float_e; | ||||||
| n = N20_SIMPLE_UNDEFINED; | ||||||
| break; | ||||||
| } | ||||||
|
|
||||||
| uint8_t header = (uint8_t)(cbor_type << 5); | ||||||
|
|
||||||
| size_t value_size = 0; | ||||||
|
|
@@ -137,9 +159,28 @@ bool n20_cbor_read_header(n20_istream_t *const s, n20_cbor_type_t *const type, u | |||||
| *type = (n20_cbor_type_t)(header >> 5); | ||||||
| uint8_t additional_info = header & 0x1f; | ||||||
|
|
||||||
| if (additional_info == 31) { | ||||||
| switch (*type) { | ||||||
| case n20_cbor_type_array_e: | ||||||
| case n20_cbor_type_map_e: | ||||||
| case n20_cbor_type_bytes_e: | ||||||
| case n20_cbor_type_string_e: | ||||||
| case n20_cbor_type_simple_float_e: | ||||||
| /* Indefinite length encoding is encoded in the ninth bit of the type. */ | ||||||
| *type = (n20_cbor_type_t)(*type + 0x100); | ||||||
| *n = 0; | ||||||
| return true; | ||||||
| default: | ||||||
| /* Additional info 31 is only valid for arrays, maps, byte strings, and text | ||||||
| * strings, and in the simple/float type denoting the end of indefinite length | ||||||
| * items. */ | ||||||
| return false; | ||||||
|
werwurm marked this conversation as resolved.
|
||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if (additional_info > 27) { | ||||||
| /* Reserved additional info value. And this code does not | ||||||
| * support indefinite length encoding (31). */ | ||||||
| /* Reserved additional info values (28-30). Indefinite length | ||||||
| * encoding (31) is handled above. */ | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -163,57 +204,141 @@ bool n20_cbor_read_header(n20_istream_t *const s, n20_cbor_type_t *const type, u | |||||
| return true; | ||||||
| } | ||||||
|
|
||||||
| bool n20_cbor_read_skip_item(n20_istream_t *const s) { | ||||||
| typedef enum n20_cbor_read_skip_item_result_s { | ||||||
| n20_cbor_read_skip_item_ok_e, | ||||||
| n20_cbor_read_skip_item_error_e, | ||||||
| n20_cbor_read_skip_item_break_e, | ||||||
| } n20_cbor_read_skip_item_result_t; | ||||||
|
|
||||||
| static n20_cbor_read_skip_item_result_t n20_cbor_read_skip_item_internal(n20_istream_t *const s); | ||||||
|
|
||||||
| static n20_cbor_read_skip_item_result_t n20_cbor_read_skip_item_map_element_internal( | ||||||
| n20_istream_t *const s) { | ||||||
| n20_cbor_read_skip_item_result_t result = n20_cbor_read_skip_item_internal(s); | ||||||
| if (result != n20_cbor_read_skip_item_ok_e) { | ||||||
| return result; | ||||||
| } | ||||||
| if (n20_cbor_read_skip_item_internal(s) != n20_cbor_read_skip_item_ok_e) { | ||||||
| /* If the second item is a terminator or if we ran out of buffer | ||||||
| * we consider it an error. */ | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| return n20_cbor_read_skip_item_ok_e; | ||||||
| } | ||||||
|
|
||||||
| static n20_cbor_read_skip_item_result_t n20_cbor_read_skip_item_stringish_chunk_internal( | ||||||
| n20_istream_t *const s, bool string) { | ||||||
| n20_cbor_type_t type; | ||||||
| uint64_t n; | ||||||
| if (!n20_cbor_read_header(s, &type, &n)) { | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| if (type == n20_cbor_type_indefinite_break_e) { | ||||||
| return n20_cbor_read_skip_item_break_e; | ||||||
| } | ||||||
| if ((string && type != n20_cbor_type_string_e) || (!string && type != n20_cbor_type_bytes_e)) { | ||||||
| return n20_cbor_read_skip_item_error_e; /* Not a valid expected stringish chunk. */ | ||||||
| } | ||||||
| if (n > SIZE_MAX) { | ||||||
| /* Prevent uncaught truncation. */ | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| if (!n20_istream_get_slice(s, NULL, n)) { | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| return n20_cbor_read_skip_item_ok_e; | ||||||
| } | ||||||
|
|
||||||
| static n20_cbor_read_skip_item_result_t n20_cbor_read_skip_item_internal(n20_istream_t *const s) { | ||||||
| n20_cbor_type_t type = n20_cbor_type_none_e; | ||||||
| uint64_t n = 0; | ||||||
| if (!n20_cbor_read_header(s, &type, &n)) { | ||||||
| return false; | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
|
|
||||||
| switch (type) { | ||||||
| case n20_cbor_type_array_e: | ||||||
| if (n > SIZE_MAX) { | ||||||
| /* Prevent overflow in the loop counter. */ | ||||||
| return false; | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| for (size_t i = 0; i < n; i++) { | ||||||
| if (!n20_cbor_read_skip_item(s)) { | ||||||
| return false; | ||||||
| if (n20_cbor_read_skip_item_internal(s) != n20_cbor_read_skip_item_ok_e) { | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| } | ||||||
| break; | ||||||
| case n20_cbor_type_map_e: | ||||||
| if (n > SIZE_MAX) { | ||||||
| /* Prevent overflow in the loop counter. */ | ||||||
| return false; | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| for (size_t i = 0; i < n; i++) { | ||||||
| if (!n20_cbor_read_skip_item(s)) { | ||||||
| return false; | ||||||
| if (n20_cbor_read_skip_item_internal(s) != n20_cbor_read_skip_item_ok_e) { | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| if (!n20_cbor_read_skip_item(s)) { | ||||||
| return false; | ||||||
| if (n20_cbor_read_skip_item_internal(s) != n20_cbor_read_skip_item_ok_e) { | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| } | ||||||
| break; | ||||||
| case n20_cbor_type_bytes_e: | ||||||
| case n20_cbor_type_string_e: { | ||||||
| if (n > SIZE_MAX) { | ||||||
| /* Prevent uncaught truncation. */ | ||||||
| return false; | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| if (!n20_istream_get_slice(s, NULL, n)) { | ||||||
| return false; | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| break; | ||||||
| } | ||||||
| case n20_cbor_type_tag_e: | ||||||
| /* Skip the tag and the item it refers to. */ | ||||||
| return n20_cbor_read_skip_item(s); | ||||||
| return n20_cbor_read_skip_item(s) ? n20_cbor_read_skip_item_ok_e | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this call the internal function like other cases?
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No this is correct. If this returns ..._break_e it needs to be an error because we cannot have a tagged break symbol. So in terms of the internal semantic it would be _internal(s) == _ok_e ? _ok_e : _error_e which is exactly what n20_cbor_read_skip_item does. |
||||||
| : n20_cbor_read_skip_item_error_e; | ||||||
| case n20_cbor_type_indefinite_bytes_e: | ||||||
| case n20_cbor_type_indefinite_string_e: { | ||||||
| n20_cbor_read_skip_item_result_t result; | ||||||
| do { | ||||||
| result = n20_cbor_read_skip_item_stringish_chunk_internal( | ||||||
| s, type == n20_cbor_type_indefinite_string_e); | ||||||
| if (result == n20_cbor_read_skip_item_error_e) { | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| } while (result != n20_cbor_read_skip_item_break_e); | ||||||
| break; | ||||||
| } | ||||||
| case n20_cbor_type_indefinite_array_e: { | ||||||
| n20_cbor_read_skip_item_result_t result; | ||||||
| do { | ||||||
| result = n20_cbor_read_skip_item_internal(s); | ||||||
| if (result == n20_cbor_read_skip_item_error_e) { | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| } while (result != n20_cbor_read_skip_item_break_e); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we have a malformed indef length array, wouldn't it be possible for the recursion to overflow?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The read functions make sure not to read past the end of the buffer. Even a definite length byte string can be larger than the buffer. In this case the read functions return false and the internal skip function returns ..._error_e. |
||||||
| break; | ||||||
| } | ||||||
| case n20_cbor_type_indefinite_map_e: { | ||||||
| n20_cbor_read_skip_item_result_t result; | ||||||
| do { | ||||||
| result = n20_cbor_read_skip_item_map_element_internal(s); | ||||||
| if (result == n20_cbor_read_skip_item_error_e) { | ||||||
| return n20_cbor_read_skip_item_error_e; | ||||||
| } | ||||||
| } while (result != n20_cbor_read_skip_item_break_e); | ||||||
| break; | ||||||
| } | ||||||
| case n20_cbor_type_indefinite_break_e: | ||||||
| return n20_cbor_read_skip_item_break_e; | ||||||
| default: | ||||||
| /* Simple values and integers have no additional data to skip. */ | ||||||
| break; | ||||||
| } | ||||||
|
|
||||||
| return true; | ||||||
| return n20_cbor_read_skip_item_ok_e; | ||||||
| } | ||||||
|
|
||||||
| bool n20_cbor_read_skip_item(n20_istream_t *const s) { | ||||||
| return n20_cbor_read_skip_item_internal(s) == n20_cbor_read_skip_item_ok_e; | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can mention that it's possible to have nested indefinite length arrays/maps, and if they're there, they also need to have the break stop codes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added tests accordingly.