COSE: Handle indefinite tstr/bstr encodings

This commit is contained in:
Brian Sipos 2021-10-03 22:28:32 -04:00 committed by Gerald Combs
parent 39036a0a30
commit bc3bced07d
5 changed files with 333 additions and 141 deletions

View File

@ -2038,6 +2038,7 @@ libwireshark.so.0 libwireshark0 #MINVER#
wscbor_chunk_mark_errors@Base 3.5.1
wscbor_chunk_read@Base 3.5.1
wscbor_error_new@Base 3.5.1
wscbor_expert_items@Base 3.5.1
wscbor_has_errors@Base 3.5.1
wscbor_init@Base 3.5.1
wscbor_is_indefinite_break@Base 3.5.1

View File

@ -369,7 +369,7 @@ static void dissect_header_pair(dissector_table_t dis_table, cose_header_context
break;
}
case CBOR_TYPE_STRING: {
const char *label = wscbor_require_tstr(wmem_packet_scope(), tvb, chunk_label);
const char *label = wscbor_require_tstr(wmem_packet_scope(), chunk_label);
item_label = proto_tree_add_cbor_tstr(tree, hf_hdr_label_tstr, pinfo, tvb, chunk_label);
if (label) {
key.label = ctx->label =
@ -467,7 +467,7 @@ static void dissect_msg_tag(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *t
static void dissect_headers(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint *offset) {
// Protected in bstr
wscbor_chunk_t *chunk_prot_bstr = wscbor_chunk_read(wmem_packet_scope(), tvb, offset);
tvbuff_t *tvb_prot = wscbor_require_bstr(tvb, chunk_prot_bstr);
tvbuff_t *tvb_prot = wscbor_require_bstr(wmem_packet_scope(), chunk_prot_bstr);
proto_item *item_prot_bstr = proto_tree_add_cbor_bstr(tree, hf_hdr_prot_bstr, pinfo, tvb, chunk_prot_bstr);
if (tvb_prot) {
proto_tree *tree_prot = proto_item_add_subtree(item_prot_bstr, ett_prot_bstr);
@ -494,13 +494,13 @@ static void dissect_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
proto_tree_add_cbor_ctrl(tree, hf_payload_null, pinfo, tvb, chunk);
}
else {
wscbor_require_bstr(tvb, chunk);
wscbor_require_bstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_bstr(tree, hf_payload_bstr, pinfo, tvb, chunk);
}
}
static void dissect_signature(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint *offset) {
wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, offset);
wscbor_require_bstr(tvb, chunk);
wscbor_require_bstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_bstr(tree, hf_signature, pinfo, tvb, chunk);
}
static void dissect_cose_signature(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint *offset) {
@ -521,7 +521,7 @@ static void dissect_ciphertext(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tr
proto_tree_add_cbor_ctrl(tree, hf_ciphertext_null, pinfo, tvb, chunk);
}
else {
wscbor_require_bstr(tvb, chunk);
wscbor_require_bstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_bstr(tree, hf_ciphertext_bstr, pinfo, tvb, chunk);
}
}
@ -557,7 +557,7 @@ static void dissect_cose_recipient(tvbuff_t *tvb, packet_info *pinfo, proto_tree
}
static void dissect_tag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint *offset) {
wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, offset);
wscbor_require_bstr(tvb, chunk);
wscbor_require_bstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_bstr(tree, hf_tag, pinfo, tvb, chunk);
}
@ -723,7 +723,7 @@ static void dissect_value_alg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
break;
}
case CBOR_TYPE_STRING: {
const char *val = wscbor_require_tstr(wmem_packet_scope(), tvb, chunk);
const char *val = wscbor_require_tstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_tstr(tree, hf_hdr_alg_tstr, pinfo, tvb, chunk);
if (value && val) {
*value = g_variant_new_string(val);
@ -739,7 +739,7 @@ static int dissect_header_salt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tr
gint offset = 0;
wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset);
wscbor_require_bstr(tvb, chunk);
wscbor_require_bstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_bstr(tree, hf_hdr_salt, pinfo, tvb, chunk);
return offset;
@ -831,7 +831,7 @@ static int dissect_header_kid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
gint offset = 0;
wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset);
wscbor_require_bstr(tvb, chunk);
wscbor_require_bstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_bstr(tree, hf_hdr_kid, pinfo, tvb, chunk);
return offset;
@ -841,7 +841,7 @@ static int dissect_header_iv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree
gint offset = 0;
wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset);
wscbor_require_bstr(tvb, chunk);
wscbor_require_bstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_bstr(tree, hf_hdr_iv, pinfo, tvb, chunk);
return offset;
@ -851,7 +851,7 @@ static int dissect_header_piv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
gint offset = 0;
wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset);
wscbor_require_bstr(tvb, chunk);
wscbor_require_bstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_bstr(tree, hf_hdr_piv, pinfo, tvb, chunk);
return offset;
@ -859,7 +859,7 @@ static int dissect_header_piv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
static void dissect_value_x5cert(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint *offset) {
wscbor_chunk_t *chunk_item = wscbor_chunk_read(wmem_packet_scope(), tvb, offset);
tvbuff_t *tvb_item = wscbor_require_bstr(tvb, chunk_item);
tvbuff_t *tvb_item = wscbor_require_bstr(wmem_packet_scope(), chunk_item);
if (tvb_item) {
// disallow column text rewrite
@ -931,7 +931,7 @@ static int dissect_header_x5t(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
dissect_value_alg(tvb, pinfo, tree_list, &offset, NULL);
wscbor_chunk_t *chunk_hash = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset);
wscbor_require_bstr(tvb, chunk_hash);
wscbor_require_bstr(wmem_packet_scope(), chunk_hash);
proto_tree_add_cbor_bstr(tree_list, hf_hdr_x5t_hash, pinfo, tvb, chunk_hash);
}
@ -1000,7 +1000,7 @@ static int dissect_keyparam_kty(tvbuff_t *tvb, packet_info *pinfo, proto_tree *t
break;
}
case CBOR_TYPE_STRING: {
const char *val = wscbor_require_tstr(wmem_packet_scope(), tvb, chunk);
const char *val = wscbor_require_tstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_tstr(tree, hf_keyparam_kty_tstr, pinfo, tvb, chunk);
if (val) {
ctx->principal = g_variant_new_string(val);
@ -1050,7 +1050,7 @@ static int dissect_keyparam_baseiv(tvbuff_t *tvb, packet_info *pinfo, proto_tree
gint offset = 0;
wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset);
wscbor_require_bstr(tvb, chunk);
wscbor_require_bstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_bstr(tree, hf_keyparam_baseiv, pinfo, tvb, chunk);
return offset;
@ -1082,7 +1082,7 @@ static int dissect_keyparam_xcoord(tvbuff_t *tvb, packet_info *pinfo, proto_tree
gint offset = 0;
wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset);
wscbor_require_bstr(tvb, chunk);
wscbor_require_bstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_bstr(tree, hf_keyparam_xcoord, pinfo, tvb, chunk);
return offset;
@ -1112,7 +1112,7 @@ static int dissect_keyparam_dcoord(tvbuff_t *tvb, packet_info *pinfo, proto_tree
gint offset = 0;
wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset);
wscbor_require_bstr(tvb, chunk);
wscbor_require_bstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_bstr(tree, hf_keyparam_dcoord, pinfo, tvb, chunk);
return offset;
@ -1122,7 +1122,7 @@ static int dissect_keyparam_k(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
gint offset = 0;
wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset);
wscbor_require_bstr(tvb, chunk);
wscbor_require_bstr(wmem_packet_scope(), chunk);
proto_tree_add_cbor_bstr(tree, hf_keyparam_k, pinfo, tvb, chunk);
return offset;

View File

@ -26,11 +26,13 @@ static expert_field ei_cbor_invalid = EI_INIT;
static expert_field ei_cbor_overflow = EI_INIT;
static expert_field ei_cbor_wrong_type = EI_INIT;
static expert_field ei_cbor_array_wrong_size = EI_INIT;
static expert_field ei_cbor_indef_string = EI_INIT;
static ei_register_info expertitems[] = {
{&ei_cbor_invalid, {"_ws.wscbor.cbor_invalid", PI_MALFORMED, PI_ERROR, "CBOR cannot be decoded", EXPFILL}},
{&ei_cbor_overflow, {"_ws.wscbor.cbor_overflow", PI_UNDECODED, PI_ERROR, "CBOR overflow of Wireshark value", EXPFILL}},
{&ei_cbor_wrong_type, {"_ws.wscbor.cbor_wrong_type", PI_MALFORMED, PI_ERROR, "CBOR is wrong type", EXPFILL}},
{&ei_cbor_array_wrong_size, {"_ws.wscbor.array_wrong_size", PI_MALFORMED, PI_WARN, "CBOR array is the wrong size", EXPFILL}},
{&ei_cbor_indef_string, {"_ws.wscbor.indef_string", PI_COMMENTS_GROUP, PI_COMMENT, "String uses indefinite-length encoding", EXPFILL}},
};
/// The basic header structure of CBOR encoding
@ -81,12 +83,18 @@ static void wscbor_read_unsigned(wscbor_head_t *head, tvbuff_t *tvb) {
}
/** Read just the CBOR head octet.
* @param alloc The allocator to use.
* @param tvb The TVB to read from.
* @param[in,out] offset The offset with in @c tvb.
* This is updated with just the head length.
* @return The new head object.
* This never returns NULL.
* @post Will throw wireshark exception if read fails.
*/
static wscbor_head_t * wscbor_head_read(wmem_allocator_t *alloc, tvbuff_t *tvb, gint start) {
static wscbor_head_t * wscbor_head_read(wmem_allocator_t *alloc, tvbuff_t *tvb, gint *offset) {
wscbor_head_t *head = wmem_new0(alloc, wscbor_head_t);
head->start = start;
head->start = *offset;
const guint8 first = tvb_get_guint8(tvb, head->start);
head->length += 1;
@ -118,6 +126,7 @@ static wscbor_head_t * wscbor_head_read(wmem_allocator_t *alloc, tvbuff_t *tvb,
break;
}
*offset += head->length;
return head;
}
@ -127,21 +136,31 @@ static void wscbor_head_free(wmem_allocator_t *alloc, wscbor_head_t *head) {
wmem_free(alloc, head);
}
struct _wscbor_chunk_priv_t {
/// The allocator used for wscbor_chunk_t.errors and wscbor_chunk_t.tags
wmem_allocator_t *alloc;
/// For string types, including indefinite length, the item payload.
/// Otherwise NULL.
tvbuff_t *str_value;
};
/** Get a clamped string length suitable for tvb functions.
* @param[in,out] chunk The chunk to read and set errors on.
* @param[in,out] chunk The chunk to set errors on.
* @param head_value The value to clamp.
* @return The clamped length value.
*/
static gint wscbor_get_length(const wscbor_chunk_t *chunk) {
static gint wscbor_get_length(wscbor_chunk_t *chunk, guint64 head_value) {
gint length;
if (chunk->head_value > G_MAXINT) {
if (head_value > G_MAXINT) {
wmem_list_append(chunk->errors, wscbor_error_new(
chunk->_alloc, &ei_cbor_overflow,
chunk->_priv->alloc, &ei_cbor_overflow,
NULL
));
length = G_MAXINT;
}
else {
length = (gint) chunk->head_value;
length = (gint) head_value;
}
return length;
}
@ -168,7 +187,8 @@ wscbor_chunk_t * wscbor_chunk_read(wmem_allocator_t *alloc, tvbuff_t *tvb, gint
DISSECTOR_ASSERT(tvb != NULL);
wscbor_chunk_t *chunk = wmem_new0(alloc, wscbor_chunk_t);
chunk->_alloc = alloc;
chunk->_priv = wmem_new0(alloc, struct _wscbor_chunk_priv_t);
chunk->_priv->alloc = alloc;
chunk->errors = wmem_list_new(alloc);
chunk->tags = wmem_list_new(alloc);
chunk->start = *offset;
@ -176,8 +196,7 @@ wscbor_chunk_t * wscbor_chunk_read(wmem_allocator_t *alloc, tvbuff_t *tvb, gint
// Read a sequence of tags followed by an item header
while (TRUE) {
// This will break out of the loop if it runs out of buffer
wscbor_head_t *head = wscbor_head_read(alloc, tvb, *offset);
*offset += head->length;
wscbor_head_t *head = wscbor_head_read(alloc, tvb, offset);
chunk->head_length += head->length;
if (head->error) {
wmem_list_append(chunk->errors, wscbor_error_new(alloc, head->error, NULL));
@ -198,25 +217,72 @@ wscbor_chunk_t * wscbor_chunk_read(wmem_allocator_t *alloc, tvbuff_t *tvb, gint
chunk->type_minor = head->type_minor;
chunk->head_value = head->rawvalue;
chunk->data_length = chunk->head_length;
switch ((cbor_type)(head->type_major)) {
case CBOR_TYPE_BYTESTRING:
case CBOR_TYPE_STRING:
if (chunk->type_minor != 31) {
const gint datalen = wscbor_get_length(chunk);
// skip over definite data
*offset += datalen;
chunk->data_length += datalen;
}
break;
default:
break;
}
wscbor_head_free(alloc, head);
break;
}
// Data beyond the tags and item head
chunk->data_length = chunk->head_length;
switch (chunk->type_major) {
case CBOR_TYPE_BYTESTRING:
case CBOR_TYPE_STRING:
if (chunk->type_minor != 31) {
const gint datalen = wscbor_get_length(chunk, chunk->head_value);
// skip over definite data
*offset += datalen;
chunk->data_length += datalen;
chunk->_priv->str_value = tvb_new_subset_length(tvb, chunk->start + chunk->head_length, datalen);
}
else {
// indefinite length, sequence of definite items
chunk->_priv->str_value = tvb_new_composite();
while (TRUE) {
wscbor_head_t *head = wscbor_head_read(alloc, tvb, offset);
chunk->data_length += head->length;
if (head->error) {
wmem_list_append(chunk->errors, wscbor_error_new(alloc, head->error, NULL));
}
const gboolean is_break = (
(head->type_major == CBOR_TYPE_FLOAT_CTRL)
&& (head->type_minor == 31)
);
if (!is_break) {
if (head->type_major != chunk->type_major) {
wmem_list_append(chunk->errors, wscbor_error_new(
chunk->_priv->alloc, &ei_cbor_wrong_type,
"Indefinite sub-string item has major type %d, should be %d",
head->type_major, chunk->type_major
));
}
else {
const gint datalen = wscbor_get_length(chunk, head->rawvalue);
*offset += datalen;
chunk->data_length += datalen;
tvb_composite_append(
chunk->_priv->str_value,
tvb_new_subset_length(tvb, head->start + head->length, datalen)
);
}
}
wscbor_head_free(alloc, head);
if (is_break) {
break;
}
}
wmem_list_append(chunk->errors, wscbor_error_new(
chunk->_priv->alloc, &ei_cbor_indef_string,
NULL
));
tvb_composite_finalize(chunk->_priv->str_value);
}
break;
default:
break;
}
return chunk;
}
@ -227,7 +293,7 @@ static void wscbor_subitem_free(gpointer data, gpointer userdata) {
void wscbor_chunk_free(wscbor_chunk_t *chunk) {
DISSECTOR_ASSERT(chunk);
wmem_allocator_t *alloc = chunk->_alloc;
wmem_allocator_t *alloc = chunk->_priv->alloc;
wmem_list_foreach(chunk->errors, wscbor_subitem_free, alloc);
wmem_destroy_list(chunk->errors);
wmem_list_foreach(chunk->tags, wscbor_subitem_free, alloc);
@ -270,11 +336,7 @@ gboolean wscbor_skip_next_item(wmem_allocator_t *alloc, tvbuff_t *tvb, gint *off
break;
case CBOR_TYPE_BYTESTRING:
case CBOR_TYPE_STRING:
if (chunk->type_minor == 31) {
// wait for indefinite break
while (!wscbor_skip_next_item(alloc, tvb, offset)) {}
}
// wscbor_read_chunk() sets offset past definite value
// wscbor_read_chunk() sets offset past string value
break;
case CBOR_TYPE_ARRAY: {
if (chunk->type_minor == 31) {
@ -334,12 +396,19 @@ void wscbor_init(void) {
expert_register_field_array(expert_wscbor, expertitems, array_length(expertitems));
}
const ei_register_info * wscbor_expert_items(int *size) {
if (size) {
*size = array_length(expertitems);
}
return expertitems;
}
gboolean wscbor_require_major_type(wscbor_chunk_t *chunk, cbor_type major) {
if (chunk->type_major == major) {
return TRUE;
}
wmem_list_append(chunk->errors, wscbor_error_new(
chunk->_alloc, &ei_cbor_wrong_type,
chunk->_priv->alloc, &ei_cbor_wrong_type,
"Item has major type %d, should be %d",
chunk->type_major, major
));
@ -356,7 +425,7 @@ gboolean wscbor_require_array_size(wscbor_chunk_t *chunk, guint64 count_min, gui
}
if ((chunk->head_value < count_min) || (chunk->head_value > count_max)) {
wmem_list_append(chunk->errors, wscbor_error_new(
chunk->_alloc, &ei_cbor_array_wrong_size,
chunk->_priv->alloc, &ei_cbor_array_wrong_size,
"Array has %" PRId64 " items, should be within [%"PRId64", %"PRId64"]",
chunk->head_value, count_min, count_max
));
@ -384,7 +453,7 @@ gboolean * wscbor_require_boolean(wmem_allocator_t *alloc, wscbor_chunk_t *chunk
}
default:
wmem_list_append(chunk->errors, wscbor_error_new(
chunk->_alloc, &ei_cbor_wrong_type,
chunk->_priv->alloc, &ei_cbor_wrong_type,
"Item has minor type %d, should be %d or %d",
chunk->type_minor, CBOR_CTRL_TRUE, CBOR_CTRL_FALSE
));
@ -412,7 +481,7 @@ gint64 * wscbor_require_int64(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) {
if (chunk->head_value > INT64_MAX) {
clamped = INT64_MAX;
wmem_list_append(chunk->errors, wscbor_error_new(
chunk->_alloc, &ei_cbor_overflow,
chunk->_priv->alloc, &ei_cbor_overflow,
NULL
));
}
@ -431,7 +500,7 @@ gint64 * wscbor_require_int64(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) {
}
default:
wmem_list_append(chunk->errors, wscbor_error_new(
chunk->_alloc, &ei_cbor_wrong_type,
chunk->_priv->alloc, &ei_cbor_wrong_type,
"Item has major type %d, should be %d or %d",
chunk->type_major, CBOR_TYPE_UINT, CBOR_TYPE_NEGINT
));
@ -440,20 +509,20 @@ gint64 * wscbor_require_int64(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) {
return result;
}
char * wscbor_require_tstr(wmem_allocator_t *alloc, tvbuff_t *parent, wscbor_chunk_t *chunk) {
char * wscbor_require_tstr(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) {
if (!wscbor_require_major_type(chunk, CBOR_TYPE_STRING)) {
return NULL;
}
return (char *)tvb_get_string_enc(alloc, parent, chunk->start + chunk->head_length, wscbor_get_length(chunk), ENC_UTF_8);
return (char *)tvb_get_string_enc(alloc, chunk->_priv->str_value, 0, tvb_reported_length(chunk->_priv->str_value), ENC_UTF_8);
}
tvbuff_t * wscbor_require_bstr(tvbuff_t *parent, wscbor_chunk_t *chunk) {
tvbuff_t * wscbor_require_bstr(wmem_allocator_t *alloc _U_, wscbor_chunk_t *chunk) {
if (!wscbor_require_major_type(chunk, CBOR_TYPE_BYTESTRING)) {
return NULL;
}
return tvb_new_subset_length(parent, chunk->start + chunk->head_length, wscbor_get_length(chunk));
return chunk->_priv->str_value;
}
proto_item * proto_tree_add_cbor_container(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) {
@ -533,14 +602,14 @@ proto_item * proto_tree_add_cbor_bitmask(proto_tree *tree, int hfindex, const gi
return item;
}
proto_item * proto_tree_add_cbor_tstr(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) {
proto_item *item = proto_tree_add_item(tree, hfindex, tvb, chunk->start + chunk->head_length, wscbor_get_length(chunk), 0);
proto_item * proto_tree_add_cbor_tstr(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb _U_, const wscbor_chunk_t *chunk) {
proto_item *item = proto_tree_add_item(tree, hfindex, chunk->_priv->str_value, 0, tvb_reported_length(chunk->_priv->str_value), 0);
wscbor_chunk_mark_errors(pinfo, item, chunk);
return item;
}
proto_item * proto_tree_add_cbor_bstr(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) {
proto_item *item = proto_tree_add_item(tree, hfindex, tvb, chunk->start + chunk->head_length, wscbor_get_length(chunk), 0);
proto_item * proto_tree_add_cbor_bstr(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb _U_, const wscbor_chunk_t *chunk) {
proto_item *item = proto_tree_add_item(tree, hfindex, chunk->_priv->str_value, 0, tvb_reported_length(chunk->_priv->str_value), 0);
wscbor_chunk_mark_errors(pinfo, item, chunk);
return item;
}

View File

@ -30,6 +30,13 @@ extern "C" {
WS_DLL_PUBLIC
void wscbor_init(void);
/** Expose available expert info for this library.
* @param[out] size Set to the size of the array.
* @return The array of expert info objects.
*/
WS_DLL_PUBLIC
const ei_register_info * wscbor_expert_items(int *size);
/// The same enumeration from libcbor-0.5
typedef enum cbor_type {
CBOR_TYPE_UINT = 0, ///< positive integers
@ -79,10 +86,12 @@ typedef struct {
guint64 value;
} wscbor_tag_t;
struct _wscbor_chunk_priv_t;
typedef struct _wscbor_chunk_priv_t wscbor_chunk_priv_t;
/// A data-containing, optionally-tagged chunk of CBOR
typedef struct {
/// The allocator used for #errors and #tags
wmem_allocator_t *_alloc;
/// Internal private data
wscbor_chunk_priv_t *_priv;
/// The start offset of this chunk
gint start;
@ -111,9 +120,11 @@ typedef struct {
* @param alloc The allocator to use.
* @param tvb The TVB to read from.
* @param[in,out] offset The offset with in @c tvb.
* This is updated to be just past the new chunk.
* @return The chunk of data found, including any errors.
* This never returns NULL.
* @post This can throw ReportedBoundsError if the read itself ran out of data.
* @post This can throw ReportedBoundsError or ContainedBoundsError
* if the read itself ran out of data.
*/
WS_DLL_PUBLIC
wscbor_chunk_t * wscbor_chunk_read(wmem_allocator_t *alloc, tvbuff_t *tvb, gint *offset);
@ -239,24 +250,30 @@ WS_DLL_PUBLIC
gint64 * wscbor_require_int64(wmem_allocator_t *alloc, wscbor_chunk_t *chunk);
/** Require a CBOR item to have a text-string value.
* If the actual text string is not needed, use the following to avoid an
* unnecessary allocation.
* @code
* wscbor_require_major_type(chunk, CBOR_TYPE_STRING)
* @endcode
*
* @param alloc The allocator to use.
* @param parent The containing buffer.
* @param[in,out] chunk The chunk to read from and write errors on.
* @return Pointer to the null-terminated UTF-8, if the item was a tstr.
* @post This can throw ContainedBoundsError string ran out of data.
*/
WS_DLL_PUBLIC
char * wscbor_require_tstr(wmem_allocator_t *alloc, tvbuff_t *parent, wscbor_chunk_t *chunk);
char * wscbor_require_tstr(wmem_allocator_t *alloc, wscbor_chunk_t *chunk);
/** Require a CBOR item to have a byte-string value.
* Use tvb_memdup() or similar if the raw byte-string is needed.
*
* @param parent The containing buffer.
* @param alloc The allocator to use.
* @param[in,out] chunk The chunk to read from and write errors on.
* @return Pointer to the value, if the item was an string.
* @return Pointer to the value, if the item was an bstr.
* The value is memory managed by wireshark.
*/
WS_DLL_PUBLIC
tvbuff_t * wscbor_require_bstr(tvbuff_t *parent, wscbor_chunk_t *chunk);
tvbuff_t * wscbor_require_bstr(wmem_allocator_t *alloc, wscbor_chunk_t *chunk);
/** Add an item representing an array or map container.
* If the item is type FT_UINT* or FT_INT* the count of (array) items

View File

@ -20,6 +20,7 @@
#include <epan/wmem_scopes.h>
#include <epan/exceptions.h>
#include <wsutil/wmem/wmem_list.h>
#include <wsutil/wmem/wmem_map.h>
#include <ws_diag_control.h>
@ -41,7 +42,9 @@ DIAG_OFF_PEDANTIC
example_s ex_uint = {1, (const guint8 *)"\x01", 1, 1, CBOR_TYPE_UINT, 1};
example_s ex_nint = {1, (const guint8 *)"\x20", 1, 1, CBOR_TYPE_NEGINT, 0};
example_s ex_bstr = {3, (const guint8 *)"\x42\x68\x69", 1, 3, CBOR_TYPE_BYTESTRING, 2};
example_s ex_bstr_indef = {6, (const guint8 *)"\x5F\x41\x68\x41\x69\xFF", 1, 6, CBOR_TYPE_BYTESTRING, 0};
example_s ex_tstr = {3, (const guint8 *)"\x62\x68\x69", 1, 3, CBOR_TYPE_STRING, 2};
example_s ex_tstr_indef = {6, (const guint8 *)"\x7F\x61\x68\x61\x69\xFF", 1, 6, CBOR_TYPE_STRING, 0};
example_s ex_false = {1, (const guint8 *)"\xF4", 1, 1, CBOR_TYPE_FLOAT_CTRL, CBOR_CTRL_FALSE};
example_s ex_true = {1, (const guint8 *)"\xF5", 1, 1, CBOR_TYPE_FLOAT_CTRL, CBOR_CTRL_TRUE};
example_s ex_null = {1, (const guint8 *)"\xF6", 1, 1, CBOR_TYPE_FLOAT_CTRL, CBOR_CTRL_NULL};
@ -57,10 +60,25 @@ DIAG_ON_PEDANTIC
static const example_s * all_examples[] = {
&ex_uint, &ex_nint,
&ex_bstr, &ex_bstr_short, &ex_tstr,
&ex_bstr, &ex_bstr_indef,
&ex_tstr, &ex_tstr_indef,
&ex_false, &ex_true, &ex_null, &ex_undef, &ex_break
};
static wmem_map_t *real_errors = NULL;
static gint has_real_errors(const wscbor_chunk_t *chunk) {
gint count = 0;
wmem_list_frame_t *frm = wmem_list_head(chunk->errors);
for (; frm; frm = wmem_list_frame_next(frm)) {
const wscbor_error_t *err = wmem_list_frame_data(frm);
if (wmem_map_contains(real_errors, err->ei)) {
count += 1;
}
}
return count;
}
/*
* These test are organized in order of the appearance, in wscbor.h, of
* the basic functions that they test. This makes it easier to
@ -71,7 +89,7 @@ static const example_s * all_examples[] = {
/* WSCBOR TESTING FUNCTIONS (/wscbor/) */
static void
wscbor_test_read_simple(void)
wscbor_test_chunk_read_simple(void)
{
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
const example_s *ex = all_examples[ex_ix];
@ -82,7 +100,7 @@ wscbor_test_read_simple(void)
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
g_assert_cmpuint(chunk->head_length, ==, ex->head_length);
g_assert_cmpuint(chunk->data_length, ==, ex->data_length);
@ -96,7 +114,7 @@ wscbor_test_read_simple(void)
}
static void
wscbor_test_read_simple_tags(void)
wscbor_test_chunk_read_simple_tags(void)
{
const guint8 *const tags = (const guint8 *)"\xC1\xD8\xC8";
tvbuff_t *tvb_tags = tvb_new_real_data(tags, 3, 3);
@ -114,7 +132,7 @@ wscbor_test_read_simple_tags(void)
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
g_assert_cmpuint(chunk->head_length, ==, ex->head_length + 3);
g_assert_cmpuint(chunk->data_length, ==, ex->data_length + 3);
@ -146,7 +164,7 @@ wscbor_test_read_simple_tags(void)
}
static void
wscbor_test_read_invalid(void)
wscbor_test_chunk_read_invalid(void)
{
tvbuff_t *tvb = tvb_new_real_data((const guint8 *)"\x00\x01\x02\xC1", 4, 2);
gint offset = 2;
@ -154,33 +172,33 @@ wscbor_test_read_invalid(void)
{ // last valid item
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
g_assert_cmpuint(chunk->type_major, ==, CBOR_TYPE_UINT);
g_assert_cmpuint(chunk->head_value, ==, 2);
wscbor_chunk_free(chunk);
}
g_assert_cmpint(offset, ==, 3);
{ // Tag without item
guint caught = 0;
volatile gulong caught = 0;
TRY {
wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(FALSE);
}
CATCH(ReportedBoundsError) {
caught = ReportedBoundsError;
CATCH_BOUNDS_ERRORS {
caught = exc->except_id.except_code;
}
ENDTRY;
g_assert_cmpuint(caught, ==, ReportedBoundsError);
}
g_assert_cmpint(offset, ==, 4);
{ // Read past the end
volatile guint caught = 0;
volatile gulong caught = 0;
TRY {
wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(FALSE);
}
CATCH(ReportedBoundsError) {
caught = ReportedBoundsError;
CATCH_BOUNDS_ERRORS {
caught = exc->except_id.except_code;
}
ENDTRY;
g_assert_cmpuint(caught, ==, ReportedBoundsError);
@ -202,17 +220,17 @@ wscbor_test_is_indefinite_break(void)
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
// this test never modifies the chunk
const gboolean val = wscbor_is_indefinite_break(chunk);
if (memcmp(ex->enc, "\xFF", 1) == 0) {
g_assert(val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
}
else {
g_assert(!val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
}
wscbor_chunk_free(chunk);
@ -221,22 +239,44 @@ wscbor_test_is_indefinite_break(void)
}
static void
wscbor_test_skip_next_item(void)
wscbor_test_skip_next_item_simple(void)
{
// skip simple items
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
const example_s *ex = all_examples[ex_ix];
printf("simple #%zu\n", ex_ix);
tvbuff_t *tvb = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
gint offset = 0;
wscbor_skip_next_item(test_scope, tvb, &offset);
g_assert_cmpint(offset, ==, ex->data_length);
tvb_free(tvb);
}
}
static void
wscbor_test_skip_next_item_multiple(void)
{
tvbuff_t *tvb = tvb_new_real_data((const guint8 *)"\x00\x01\x02\x03", 4, 4);
gint offset = 3;
gint offset = 2;
gboolean res = wscbor_skip_next_item(test_scope, tvb, &offset);
g_assert(!res);
g_assert_cmpint(offset, ==, 3);
res = wscbor_skip_next_item(test_scope, tvb, &offset);
g_assert(!res);
g_assert_cmpint(offset, ==, 4);
{ // Read past the end
guint caught = 0;
volatile gulong caught = 0;
TRY {
wscbor_skip_next_item(test_scope, tvb, &offset);
g_assert(FALSE);
}
CATCH(ReportedBoundsError) {
caught = ReportedBoundsError;
CATCH_BOUNDS_ERRORS {
caught = exc->except_id.except_code;
}
ENDTRY;
g_assert_cmpuint(caught, ==, ReportedBoundsError);
@ -258,14 +298,14 @@ wscbor_test_require_major_type(void)
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
g_assert(wscbor_require_major_type(chunk, ex->type_major));
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
// any other type
g_assert(!wscbor_require_major_type(chunk, ex->type_major + 1));
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
g_assert_cmpuint(has_real_errors(chunk), ==, 1);
wscbor_chunk_free(chunk);
tvb_free(tvb);
@ -273,7 +313,7 @@ wscbor_test_require_major_type(void)
}
static void
wscbor_test_require_boolean(void)
wscbor_test_require_boolean_simple(void)
{
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
const example_s *ex = all_examples[ex_ix];
@ -284,18 +324,18 @@ wscbor_test_require_boolean(void)
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
const gboolean *val = wscbor_require_boolean(test_scope, chunk);
if ((ex->type_major == CBOR_TYPE_FLOAT_CTRL)
&& ((ex->head_value == CBOR_CTRL_FALSE) || (ex->head_value == CBOR_CTRL_TRUE))) {
g_assert(val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
g_assert_cmpint(*val, ==, ex->head_value == CBOR_CTRL_TRUE);
}
else {
g_assert(!val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
g_assert_cmpuint(has_real_errors(chunk), ==, 1);
}
wscbor_chunk_free(chunk);
@ -304,7 +344,7 @@ wscbor_test_require_boolean(void)
}
static void
wscbor_test_require_int64(void)
wscbor_test_require_int64_simple(void)
{
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
const example_s *ex = all_examples[ex_ix];
@ -315,22 +355,22 @@ wscbor_test_require_int64(void)
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
const gint64 *val = wscbor_require_int64(test_scope, chunk);
if (ex->type_major == CBOR_TYPE_UINT) {
g_assert(val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
g_assert_cmpint(*val, ==, ex->head_value);
}
else if (ex->type_major == CBOR_TYPE_NEGINT) {
g_assert(val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
g_assert_cmpint(*val, ==, -1 - ex->head_value);
}
else {
g_assert(!val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
g_assert_cmpuint(has_real_errors(chunk), ==, 1);
}
wscbor_chunk_free(chunk);
@ -353,24 +393,24 @@ wscbor_test_require_int64_overflow(void)
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
g_assert_cmpuint(chunk->type_major, ==, ex->type_major);
g_assert_cmpuint(chunk->head_value, ==, ex->head_value);
const gint64 *val = wscbor_require_int64(test_scope, chunk);
if (ex->type_major == CBOR_TYPE_UINT) {
g_assert(val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
g_assert_cmpuint(has_real_errors(chunk), ==, 1);
g_assert_cmpint(*val, ==, G_MAXINT64);
}
else if (ex->type_major == CBOR_TYPE_NEGINT) {
g_assert(val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
g_assert_cmpuint(has_real_errors(chunk), ==, 1);
g_assert_cmpint(*val, ==, G_MININT64);
}
else {
g_assert(!val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
g_assert_cmpuint(has_real_errors(chunk), ==, 1);
}
wscbor_chunk_free(chunk);
@ -379,7 +419,7 @@ wscbor_test_require_int64_overflow(void)
}
static void
wscbor_test_require_tstr(void)
wscbor_test_require_tstr_simple(void)
{
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
const example_s *ex = all_examples[ex_ix];
@ -390,18 +430,20 @@ wscbor_test_require_tstr(void)
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
const char *val = wscbor_require_tstr(test_scope, tvb, chunk);
const char *val = wscbor_require_tstr(test_scope, chunk);
if (ex->type_major == CBOR_TYPE_STRING) {
g_assert(val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
// only works because this is Latin-1 text
g_assert_cmpint(strlen(val), ==, ex->head_value);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
if (ex->head_value > 0) {
// only works because this is Latin-1 text
g_assert_cmpmem(val, strlen(val), ex->enc + ex->head_length, ex->head_value);
}
}
else {
g_assert(!val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
g_assert_cmpuint(has_real_errors(chunk), ==, 1);
}
wscbor_chunk_free(chunk);
@ -427,20 +469,20 @@ wscbor_test_require_tstr_short(void)
chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
g_assert_cmpuint(chunk->type_major, ==, ex->type_major);
g_assert_cmpuint(chunk->head_value, ==, ex->head_value);
volatile guint caught = 0;
volatile gulong caught = 0;
TRY {
wscbor_require_tstr(test_scope, tvb, chunk);
wscbor_require_tstr(test_scope, chunk);
g_assert(FALSE);
}
CATCH(ReportedBoundsError) {
caught = ReportedBoundsError;
CATCH_BOUNDS_ERRORS {
caught = exc->except_id.except_code;
}
ENDTRY;
g_assert_cmpuint(caught, ==, ReportedBoundsError);
g_assert_cmpuint(caught, ==, ContainedBoundsError);
wscbor_chunk_free(chunk);
tvb_free(tvb);
@ -448,7 +490,7 @@ wscbor_test_require_tstr_short(void)
}
static void
wscbor_test_require_bstr(void)
wscbor_test_require_bstr_simple(void)
{
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
const example_s *ex = all_examples[ex_ix];
@ -459,18 +501,25 @@ wscbor_test_require_bstr(void)
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
const tvbuff_t *val = wscbor_require_bstr(tvb, chunk);
tvbuff_t *val = wscbor_require_bstr(test_scope, chunk);
if (ex->type_major == CBOR_TYPE_BYTESTRING) {
g_assert(val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
g_assert_cmpint(tvb_reported_length(val), ==, ex->head_value);
g_assert_cmpuint(tvb_captured_length(val), <=, ex->head_value);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
if (ex->head_value > 0) {
g_assert_cmpint(tvb_reported_length(val), ==, ex->head_value);
g_assert_cmpuint(tvb_captured_length(val), ==, ex->head_value);
const gint buflen = tvb_reported_length(val);
void *buf = tvb_memdup(test_scope, val, 0, buflen);
g_assert(buf);
g_assert_cmpmem(buf, buflen, ex->enc + ex->head_length, ex->head_value);
}
}
else {
g_assert(!val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
g_assert_cmpuint(has_real_errors(chunk), ==, 1);
}
wscbor_chunk_free(chunk);
@ -478,6 +527,49 @@ wscbor_test_require_bstr(void)
}
}
static void
wscbor_test_require_bstr_short(void)
{
const example_s * examples[] = {
&ex_bstr_short,
};
for (size_t ex_ix = 0; ex_ix < (sizeof(examples) / sizeof(example_s*)); ++ex_ix) {
const example_s *ex = examples[ex_ix];
printf("simple #%zu\n", ex_ix);
tvbuff_t *tvb = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
gint offset = 0;
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
g_assert_cmpuint(chunk->type_major, ==, ex->type_major);
g_assert_cmpuint(chunk->head_value, ==, ex->head_value);
// no exception, but truncated captured length
tvbuff_t *val = wscbor_require_bstr(test_scope, chunk);
g_assert(val);
g_assert_cmpuint(has_real_errors(chunk), ==, 0);
g_assert_cmpint(tvb_reported_length(val), ==, ex->head_value);
g_assert_cmpuint(tvb_captured_length(val), <, ex->head_value);
volatile gulong caught = 0;
TRY {
const gint buflen = tvb_reported_length(val);
tvb_memdup(test_scope, val, 0, buflen);
g_assert(FALSE);
}
CATCH_BOUNDS_ERRORS {
caught = exc->except_id.except_code;
}
ENDTRY;
g_assert_cmpuint(caught, ==, ContainedBoundsError);
wscbor_chunk_free(chunk);
tvb_free(tvb);
}
}
static void
wscbor_test_require_bstr_overflow(void)
{
@ -493,13 +585,13 @@ wscbor_test_require_bstr_overflow(void)
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
g_assert(chunk);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
g_assert_cmpuint(has_real_errors(chunk), ==, 1);
g_assert_cmpuint(chunk->type_major, ==, ex->type_major);
g_assert_cmpuint(chunk->head_value, ==, ex->head_value);
const tvbuff_t *val = wscbor_require_bstr(tvb, chunk);
const tvbuff_t *val = wscbor_require_bstr(test_scope, chunk);
g_assert(val);
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 2);
g_assert_cmpuint(has_real_errors(chunk), ==, 1);
g_assert_cmpuint(tvb_reported_length(val), ==, G_MAXINT);
g_assert_cmpuint(tvb_captured_length(val), ==, 2);
@ -515,22 +607,35 @@ main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
g_test_add_func("/wscbor/read/simple", wscbor_test_read_simple);
g_test_add_func("/wscbor/read/simple_tags", wscbor_test_read_simple_tags);
g_test_add_func("/wscbor/read/invalid", wscbor_test_read_invalid);
g_test_add_func("/wscbor/chunk_read/simple", wscbor_test_chunk_read_simple);
g_test_add_func("/wscbor/chunk_read/simple_tags", wscbor_test_chunk_read_simple_tags);
g_test_add_func("/wscbor/chunk_read/invalid", wscbor_test_chunk_read_invalid);
g_test_add_func("/wscbor/is_indefinite_break", wscbor_test_is_indefinite_break);
g_test_add_func("/wscbor/skip_next_item", wscbor_test_skip_next_item);
g_test_add_func("/wscbor/skip_next_item/simple", wscbor_test_skip_next_item_simple);
g_test_add_func("/wscbor/skip_next_item/multiple", wscbor_test_skip_next_item_multiple);
g_test_add_func("/wscbor/require_major_type", wscbor_test_require_major_type);
g_test_add_func("/wscbor/require_boolean", wscbor_test_require_boolean);
g_test_add_func("/wscbor/require_int64", wscbor_test_require_int64);
g_test_add_func("/wscbor/require_int64_overflow", wscbor_test_require_int64_overflow);
g_test_add_func("/wscbor/require_tstr", wscbor_test_require_tstr);
g_test_add_func("/wscbor/require_tstr_short", wscbor_test_require_tstr_short);
g_test_add_func("/wscbor/require_bstr", wscbor_test_require_bstr);
g_test_add_func("/wscbor/require_bstr_overflow", wscbor_test_require_bstr_overflow);
g_test_add_func("/wscbor/require_boolean/simple", wscbor_test_require_boolean_simple);
g_test_add_func("/wscbor/require_int64/simple", wscbor_test_require_int64_simple);
g_test_add_func("/wscbor/require_int64/overflow", wscbor_test_require_int64_overflow);
g_test_add_func("/wscbor/require_tstr/simple", wscbor_test_require_tstr_simple);
g_test_add_func("/wscbor/require_tstr/short", wscbor_test_require_tstr_short);
g_test_add_func("/wscbor/require_bstr/simple", wscbor_test_require_bstr_simple);
g_test_add_func("/wscbor/require_bstr/short", wscbor_test_require_bstr_short);
g_test_add_func("/wscbor/require_bstr/overflow", wscbor_test_require_bstr_overflow);
wmem_init_scopes();
test_scope = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
{
// Extract high-severity errors
int ex_size = 0;
const ei_register_info *ex_items = wscbor_expert_items(&ex_size);
real_errors = wmem_map_new(test_scope, g_direct_hash, g_direct_equal);
for (int ix = 0; ix < ex_size; ++ix) {
if (ex_items[ix].eiinfo.severity & (PI_NOTE|PI_WARN|PI_ERROR)) {
wmem_map_insert(real_errors, ex_items[ix].ids, NULL);
}
}
}
//cannot use: wscbor_init();
result = g_test_run();
//none needed: wscbor_cleanup();