diff --git a/include/ctraces/ctr_decode_msgpack.h b/include/ctraces/ctr_decode_msgpack.h index e4689aa..0cc064c 100644 --- a/include/ctraces/ctr_decode_msgpack.h +++ b/include/ctraces/ctr_decode_msgpack.h @@ -21,8 +21,10 @@ #define CTR_DECODE_MSGPACK_H #include +#include #define CTR_DECODE_MSGPACK_SUCCESS (CTR_MPACK_SUCCESS) +#define CTR_DECODE_MSGPACK_INSUFFICIENT_DATA (CTR_MPACK_INSUFFICIENT_DATA) #define CTR_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR (CTR_MPACK_INVALID_ARGUMENT_ERROR) #define CTR_DECODE_MSGPACK_INVALID_STATE (CTR_MPACK_ERROR_CUTOFF + 1) #define CTR_DECODE_MSGPACK_ALLOCATION_ERROR (CTR_MPACK_ERROR_CUTOFF + 2) diff --git a/include/ctraces/ctr_resource.h b/include/ctraces/ctr_resource.h index 772931c..6571354 100644 --- a/include/ctraces/ctr_resource.h +++ b/include/ctraces/ctr_resource.h @@ -32,6 +32,7 @@ struct ctrace_resource_span { struct cfl_list scope_spans; cfl_sds_t schema_url; struct cfl_list _head; /* link to ctraces->resource_span list */ + struct ctrace *ctx; /* owning trace context */ }; /* resource */ diff --git a/include/ctraces/ctr_span.h b/include/ctraces/ctr_span.h index ffbb4aa..d3d97e6 100644 --- a/include/ctraces/ctr_span.h +++ b/include/ctraces/ctr_span.h @@ -68,7 +68,7 @@ struct ctrace_span { struct ctrace_id *span_id; /* the unique span ID */ struct ctrace_id *parent_span_id; /* any parent ? a NULL means a root span */ cfl_sds_t trace_state; /* trace state */ - int32_t flags; /* flags */ + uint32_t flags; /* flags */ cfl_sds_t name; /* user-name assigned */ @@ -158,6 +158,8 @@ void ctr_span_event_delete(struct ctrace_span_event *event); int ctr_span_event_set_attribute_string(struct ctrace_span_event *event, char *key, char *value); int ctr_span_event_set_attribute_bool(struct ctrace_span_event *event, char *key, int b); int ctr_span_event_set_attribute_int(struct ctrace_span_event *event, char *key, int value); +int ctr_span_event_set_attribute_int64(struct ctrace_span_event *event, char *key, + int64_t value); int ctr_span_event_set_attribute_double(struct ctrace_span_event *event, char *key, double value); int ctr_span_event_set_attribute_array(struct ctrace_span_event *event, char *key, struct cfl_array *value); diff --git a/include/ctraces/ctr_variant_utils.h b/include/ctraces/ctr_variant_utils.h index 029950c..ce5ccbe 100644 --- a/include/ctraces/ctr_variant_utils.h +++ b/include/ctraces/ctr_variant_utils.h @@ -21,10 +21,14 @@ #define CTR_VARIANT_UTILS_H #include +#include #define CFL_VARIANT_UTILS_MAXIMUM_FIXED_ARRAY_SIZE 100 #define CFL_VARIANT_UTILS_INITIAL_ARRAY_SIZE 100 #define CFL_VARIANT_UTILS_SERIALIZED_ARRAY_SIZE_LIMIT 100000 +#define CFL_VARIANT_UTILS_SERIALIZED_MAP_SIZE_LIMIT 100000 +#define CFL_VARIANT_UTILS_MAXIMUM_NESTING_DEPTH 64 +#define CFL_VARIANT_UTILS_MAXIMUM_KEY_LENGTH (1024 * 1000) /* These are the only functions meant for general use, * the reason why the kvlist packing and unpacking @@ -51,6 +55,10 @@ static inline int pack_cfl_variant_kvlist(mpack_writer_t *writer, static inline int unpack_cfl_variant(mpack_reader_t *reader, struct cfl_variant **value); +static inline int unpack_cfl_variant_depth(mpack_reader_t *reader, + struct cfl_variant **value, + size_t depth); + static inline int unpack_cfl_kvlist(mpack_reader_t *reader, struct cfl_kvlist **result_kvlist); @@ -88,6 +96,14 @@ static inline int pack_cfl_variant_int64(mpack_writer_t *writer, return 0; } +static inline int pack_cfl_variant_uint64(mpack_writer_t *writer, + uint64_t value) +{ + mpack_write_u64(writer, value); + + return 0; +} + static inline int pack_cfl_variant_double(mpack_writer_t *writer, double value) { @@ -169,6 +185,9 @@ static inline int pack_cfl_variant(mpack_writer_t *writer, else if (value->type == CFL_VARIANT_INT) { result = pack_cfl_variant_int64(writer, value->data.as_int64); } + else if (value->type == CFL_VARIANT_UINT) { + result = pack_cfl_variant_uint64(writer, value->data.as_uint64); + } else if (value->type == CFL_VARIANT_DOUBLE) { result = pack_cfl_variant_double(writer, value->data.as_double); } @@ -183,8 +202,12 @@ static inline int pack_cfl_variant(mpack_writer_t *writer, value->data.as_bytes, cfl_sds_len(value->data.as_bytes)); } + else if (value->type == CFL_VARIANT_NULL) { + mpack_write_nil(writer); + result = 0; + } else if (value->type == CFL_VARIANT_REFERENCE) { - result = pack_cfl_variant_string(writer, value->data.as_string); + result = -1; } else { result = -1; @@ -212,8 +235,9 @@ static inline int unpack_cfl_variant_read_tag(mpack_reader_t *reader, return 0; } -static inline int unpack_cfl_array(mpack_reader_t *reader, - struct cfl_array **result_array) +static inline int unpack_cfl_array_depth(mpack_reader_t *reader, + struct cfl_array **result_array, + size_t depth) { struct cfl_array *internal_array; size_t entry_count; @@ -250,7 +274,7 @@ static inline int unpack_cfl_array(mpack_reader_t *reader, } for (index = 0 ; index < entry_count ; index++) { - result = unpack_cfl_variant(reader, &entry_value); + result = unpack_cfl_variant_depth(reader, &entry_value, depth + 1); if (result != 0) { cfl_array_destroy(internal_array); @@ -280,11 +304,12 @@ static inline int unpack_cfl_array(mpack_reader_t *reader, return 0; } -static inline int unpack_cfl_kvlist(mpack_reader_t *reader, - struct cfl_kvlist **result_kvlist) +static inline int unpack_cfl_kvlist_depth(mpack_reader_t *reader, + struct cfl_kvlist **result_kvlist, + size_t depth) { struct cfl_kvlist *internal_kvlist; - char key_name[256]; + char *key_name; size_t entry_count; size_t key_length; struct cfl_variant *key_value; @@ -301,6 +326,10 @@ static inline int unpack_cfl_kvlist(mpack_reader_t *reader, entry_count = mpack_tag_map_count(&tag); + if (entry_count >= CFL_VARIANT_UTILS_SERIALIZED_MAP_SIZE_LIMIT) { + return -2; + } + internal_kvlist = cfl_kvlist_create(); if (internal_kvlist == NULL) { @@ -309,6 +338,7 @@ static inline int unpack_cfl_kvlist(mpack_reader_t *reader, result = 0; key_value = NULL; + key_name = NULL; for (index = 0 ; index < entry_count ; index++) { result = unpack_cfl_variant_read_tag(reader, &key_tag, mpack_type_str); @@ -321,13 +351,19 @@ static inline int unpack_cfl_kvlist(mpack_reader_t *reader, key_length = mpack_tag_str_length(&key_tag); - if (key_length >= sizeof(key_name)) { + if (key_length > CFL_VARIANT_UTILS_MAXIMUM_KEY_LENGTH) { result = -5; break; } - mpack_read_cstr(reader, key_name, sizeof(key_name), key_length); + key_name = malloc(key_length + 1); + if (key_name == NULL) { + result = -3; + break; + } + + mpack_read_cstr(reader, key_name, key_length + 1, key_length); key_name[key_length] = '\0'; @@ -339,7 +375,7 @@ static inline int unpack_cfl_kvlist(mpack_reader_t *reader, break; } - result = unpack_cfl_variant(reader, &key_value); + result = unpack_cfl_variant_depth(reader, &key_value, depth + 1); if (result != 0) { printf("VARIANT UNPACK ERROR : [%s] = %d\n", key_name, result); @@ -350,6 +386,9 @@ static inline int unpack_cfl_kvlist(mpack_reader_t *reader, result = cfl_kvlist_insert(internal_kvlist, key_name, key_value); + free(key_name); + key_name = NULL; + if (result != 0) { result = -8; @@ -366,6 +405,7 @@ static inline int unpack_cfl_kvlist(mpack_reader_t *reader, } if (result != 0) { + free(key_name); cfl_kvlist_destroy(internal_kvlist); if (key_value != NULL) { @@ -416,6 +456,7 @@ static inline int unpack_cfl_variant_string(mpack_reader_t *reader, *value = cfl_variant_create_from_reference(value_data); if (*value == NULL) { + cfl_sds_destroy(value_data); return -5; } @@ -462,6 +503,7 @@ static inline int unpack_cfl_variant_binary(mpack_reader_t *reader, *value = cfl_variant_create_from_reference(value_data); if (*value == NULL) { + cfl_sds_destroy(value_data); return -5; } @@ -504,7 +546,7 @@ static inline int unpack_cfl_variant_uint64(mpack_reader_t *reader, return result; } - *value = cfl_variant_create_from_int64((int64_t) mpack_tag_uint_value(&tag)); + *value = cfl_variant_create_from_uint64(mpack_tag_uint_value(&tag)); if (*value == NULL) { return -3; @@ -555,13 +597,33 @@ static inline int unpack_cfl_variant_double(mpack_reader_t *reader, return 0; } -static inline int unpack_cfl_variant_array(mpack_reader_t *reader, - struct cfl_variant **value) +static inline int unpack_cfl_variant_null(mpack_reader_t *reader, + struct cfl_variant **value) +{ + int result; + mpack_tag_t tag; + + result = unpack_cfl_variant_read_tag(reader, &tag, mpack_type_nil); + if (result != 0) { + return result; + } + + *value = cfl_variant_create_from_null(); + if (*value == NULL) { + return -3; + } + + return 0; +} + +static inline int unpack_cfl_variant_array_depth(mpack_reader_t *reader, + struct cfl_variant **value, + size_t depth) { struct cfl_array *unpacked_array; int result; - result = unpack_cfl_array(reader, &unpacked_array); + result = unpack_cfl_array_depth(reader, &unpacked_array, depth); if (result != 0) { return result; @@ -570,19 +632,21 @@ static inline int unpack_cfl_variant_array(mpack_reader_t *reader, *value = cfl_variant_create_from_array(unpacked_array); if (*value == NULL) { + cfl_array_destroy(unpacked_array); return -3; } return 0; } -static inline int unpack_cfl_variant_kvlist(mpack_reader_t *reader, - struct cfl_variant **value) +static inline int unpack_cfl_variant_kvlist_depth(mpack_reader_t *reader, + struct cfl_variant **value, + size_t depth) { struct cfl_kvlist *unpacked_kvlist; int result; - result = unpack_cfl_kvlist(reader, &unpacked_kvlist); + result = unpack_cfl_kvlist_depth(reader, &unpacked_kvlist, depth); if (result != 0) { return result; @@ -591,19 +655,25 @@ static inline int unpack_cfl_variant_kvlist(mpack_reader_t *reader, *value = cfl_variant_create_from_kvlist(unpacked_kvlist); if (*value == NULL) { + cfl_kvlist_destroy(unpacked_kvlist); return -3; } return 0; } -static inline int unpack_cfl_variant(mpack_reader_t *reader, - struct cfl_variant **value) +static inline int unpack_cfl_variant_depth(mpack_reader_t *reader, + struct cfl_variant **value, + size_t depth) { mpack_type_t value_type; int result; mpack_tag_t tag; + if (depth >= CFL_VARIANT_UTILS_MAXIMUM_NESTING_DEPTH) { + return -1; + } + tag = mpack_peek_tag(reader); if (mpack_ok != mpack_reader_error(reader)) { @@ -628,14 +698,17 @@ static inline int unpack_cfl_variant(mpack_reader_t *reader, result = unpack_cfl_variant_double(reader, value); } else if (value_type == mpack_type_array) { - result = unpack_cfl_variant_array(reader, value); + result = unpack_cfl_variant_array_depth(reader, value, depth); } else if (value_type == mpack_type_map) { - result = unpack_cfl_variant_kvlist(reader, value); + result = unpack_cfl_variant_kvlist_depth(reader, value, depth); } else if (value_type == mpack_type_bin) { result = unpack_cfl_variant_binary(reader, value); } + else if (value_type == mpack_type_nil) { + result = unpack_cfl_variant_null(reader, value); + } else { result = -1; } @@ -643,4 +716,22 @@ static inline int unpack_cfl_variant(mpack_reader_t *reader, return result; } +static inline int unpack_cfl_array(mpack_reader_t *reader, + struct cfl_array **result_array) +{ + return unpack_cfl_array_depth(reader, result_array, 0); +} + +static inline int unpack_cfl_kvlist(mpack_reader_t *reader, + struct cfl_kvlist **result_kvlist) +{ + return unpack_cfl_kvlist_depth(reader, result_kvlist, 0); +} + +static inline int unpack_cfl_variant(mpack_reader_t *reader, + struct cfl_variant **value) +{ + return unpack_cfl_variant_depth(reader, value, 0); +} + #endif diff --git a/src/ctr_decode_msgpack.c b/src/ctr_decode_msgpack.c index af8ee3c..f1ed4e5 100644 --- a/src/ctr_decode_msgpack.c +++ b/src/ctr_decode_msgpack.c @@ -306,6 +306,13 @@ static int unpack_link_dropped_attributes_count(mpack_reader_t *reader, size_t i return ctr_mpack_consume_uint32_tag(reader, &context->link->dropped_attr_count); } +static int unpack_link_flags(mpack_reader_t *reader, size_t index, void *ctx) +{ + struct ctr_msgpack_decode_context *context = ctx; + + return ctr_mpack_consume_uint32_tag(reader, &context->link->flags); +} + static int unpack_link_attributes(mpack_reader_t *reader, size_t index, void *ctx) { struct ctr_msgpack_decode_context *context = ctx; @@ -321,6 +328,10 @@ static int unpack_link_attributes(mpack_reader_t *reader, size_t index, void *ct if (result == 0) { if (context->link->attr == NULL) { context->link->attr = ctr_attributes_create(); + if (context->link->attr == NULL) { + cfl_kvlist_destroy(attributes); + return CTR_DECODE_MSGPACK_ALLOCATION_ERROR; + } } if (context->link->attr->kv != NULL) { @@ -349,6 +360,7 @@ static int unpack_link(mpack_reader_t *reader, size_t index, void *ctx) {"trace_state", unpack_link_trace_state}, {"attributes", unpack_link_attributes}, {"dropped_attributes_count", unpack_link_dropped_attributes_count}, + {"flags", unpack_link_flags}, {NULL, NULL} }; @@ -376,9 +388,12 @@ static int unpack_span_trace_id(mpack_reader_t *reader, size_t index, void *ctx) decoded_id = ctr_id_from_base16(value); if (decoded_id != NULL) { - ctr_span_set_trace_id_with_cid(context->span, decoded_id); + result = ctr_span_set_trace_id_with_cid(context->span, decoded_id); ctr_id_destroy(decoded_id); + if (result != 0) { + result = CTR_DECODE_MSGPACK_ALLOCATION_ERROR; + } } else { result = CTR_MPACK_CORRUPT_INPUT_DATA_ERROR; @@ -403,9 +418,12 @@ static int unpack_span_span_id(mpack_reader_t *reader, size_t index, void *ctx) decoded_id = ctr_id_from_base16(value); if (decoded_id != NULL) { - ctr_span_set_span_id_with_cid(context->span, decoded_id); + result = ctr_span_set_span_id_with_cid(context->span, decoded_id); ctr_id_destroy(decoded_id); + if (result != 0) { + result = CTR_DECODE_MSGPACK_ALLOCATION_ERROR; + } } else { result = CTR_MPACK_CORRUPT_INPUT_DATA_ERROR; @@ -430,9 +448,12 @@ static int unpack_span_parent_span_id(mpack_reader_t *reader, size_t index, void decoded_id = ctr_id_from_base16(value); if (decoded_id != NULL) { - ctr_span_set_parent_span_id_with_cid(context->span, decoded_id); + result = ctr_span_set_parent_span_id_with_cid(context->span, decoded_id); ctr_id_destroy(decoded_id); + if (result != 0) { + result = CTR_DECODE_MSGPACK_ALLOCATION_ERROR; + } } else { result = CTR_MPACK_CORRUPT_INPUT_DATA_ERROR; @@ -457,6 +478,20 @@ static int unpack_span_trace_state(mpack_reader_t *reader, size_t index, void *c return ctr_mpack_consume_string_or_nil_tag(reader, &context->span->trace_state); } +static int unpack_span_flags(mpack_reader_t *reader, size_t index, void *ctx) +{ + struct ctr_msgpack_decode_context *context = ctx; + uint32_t flags; + int result; + + result = ctr_mpack_consume_uint32_tag(reader, &flags); + if (result == CTR_MPACK_SUCCESS) { + context->span->flags = flags; + } + + return result; +} + static int unpack_span_name(mpack_reader_t *reader, size_t index, void *ctx) { struct ctr_msgpack_decode_context *context = ctx; @@ -473,8 +508,15 @@ static int unpack_span_name(mpack_reader_t *reader, size_t index, void *ctx) static int unpack_span_kind(mpack_reader_t *reader, size_t index, void *ctx) { struct ctr_msgpack_decode_context *context = ctx; + int32_t kind; + int result; + + result = ctr_mpack_consume_int32_tag(reader, &kind); + if (result == CTR_MPACK_SUCCESS && ctr_span_kind_set(context->span, kind) != 0) { + return CTR_MPACK_CORRUPT_INPUT_DATA_ERROR; + } - return ctr_mpack_consume_int32_tag(reader, &context->span->kind); + return result; } static int unpack_span_start_time_unix_nano(mpack_reader_t *reader, size_t index, void *ctx) @@ -550,8 +592,18 @@ static int unpack_span_links(mpack_reader_t *reader, size_t index, void *ctx) static int unpack_span_status_code(mpack_reader_t *reader, size_t index, void *ctx) { struct ctr_msgpack_decode_context *context = ctx; + int32_t code; + int result; - return ctr_mpack_consume_int32_tag(reader, &context->span->status.code); + result = ctr_mpack_consume_int32_tag(reader, &code); + if (result == CTR_MPACK_SUCCESS) { + if (code < CTRACE_SPAN_STATUS_CODE_UNSET || code > CTRACE_SPAN_STATUS_CODE_ERROR) { + return CTR_MPACK_CORRUPT_INPUT_DATA_ERROR; + } + context->span->status.code = code; + } + + return result; } static int unpack_span_status_message(mpack_reader_t *reader, size_t index, void *ctx) @@ -590,6 +642,7 @@ static int unpack_span(mpack_reader_t *reader, size_t index, void *ctx) {"span_id", unpack_span_span_id}, {"parent_span_id", unpack_span_parent_span_id}, {"trace_state", unpack_span_trace_state}, + {"flags", unpack_span_flags}, {"name", unpack_span_name}, {"kind", unpack_span_kind}, {"start_time_unix_nano", unpack_span_start_time_unix_nano}, @@ -733,6 +786,16 @@ int ctr_decode_msgpack_create(struct ctrace **out_context, char *in_buf, size_t mpack_reader_t reader; int result; + if (out_context == NULL || in_buf == NULL || offset == NULL) { + return CTR_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + *out_context = NULL; + + if (*offset >= in_size) { + return CTR_DECODE_MSGPACK_INSUFFICIENT_DATA; + } + memset(&context, 0, sizeof(context)); context.trace = ctr_create(NULL); diff --git a/src/ctr_decode_opentelemetry.c b/src/ctr_decode_opentelemetry.c index bf0fa28..162c13e 100644 --- a/src/ctr_decode_opentelemetry.c +++ b/src/ctr_decode_opentelemetry.c @@ -313,7 +313,8 @@ static int convert_bytes_value(struct opentelemetry_decode_value *ctr_val, switch (value_type) { case CTR_OPENTELEMETRY_TYPE_ATTRIBUTE: - result = -1; + result = cfl_kvlist_insert_bytes(ctr_val->ctr_attr->kv, key, + buf, len, CFL_FALSE); break; case CTR_OPENTELEMETRY_TYPE_ARRAY: @@ -532,15 +533,15 @@ static int resource_set_data(struct ctrace_resource *resource, return 0; } -void ctr_scope_span_set_scope(struct ctrace_scope_span *scope_span, - Opentelemetry__Proto__Common__V1__InstrumentationScope *scope) +static int ctr_scope_span_set_scope(struct ctrace_scope_span *scope_span, + Opentelemetry__Proto__Common__V1__InstrumentationScope *scope) { struct ctrace_attributes *ctr_attributes; struct ctrace_instrumentation_scope *ins_scope; ctr_attributes = convert_otel_attrs(scope->n_attributes, scope->attributes); if (ctr_attributes == NULL) { - return; + return -1; } ins_scope = ctr_instrumentation_scope_create(scope->name, scope->version, @@ -548,14 +549,16 @@ void ctr_scope_span_set_scope(struct ctrace_scope_span *scope_span, ctr_attributes); if (!ins_scope) { ctr_attributes_destroy(ctr_attributes); - return; + return -1; } ctr_scope_span_set_instrumentation_scope(scope_span, ins_scope); + + return 0; } -void ctr_span_set_links(struct ctrace_span *ctr_span, size_t n_links, - Opentelemetry__Proto__Trace__V1__Span__Link **links) +static int ctr_span_set_links(struct ctrace_span *ctr_span, size_t n_links, + Opentelemetry__Proto__Trace__V1__Span__Link **links) { int index_link; struct ctrace_link *ctr_link; @@ -563,7 +566,7 @@ void ctr_span_set_links(struct ctrace_span *ctr_span, size_t n_links, Opentelemetry__Proto__Trace__V1__Span__Link *link; if (n_links > 0 && links == NULL) { - return; + return -1; } for (index_link = 0; index_link < n_links; index_link++) { @@ -578,19 +581,37 @@ void ctr_span_set_links(struct ctrace_span *ctr_span, size_t n_links, link->span_id.data, link->span_id.len); if (ctr_link == NULL) { - return; + return -1; } ctr_attributes = convert_otel_attrs(link->n_attributes, link->attributes); if (ctr_attributes == NULL) { - return; + return -1; } ctr_link->attr = ctr_attributes; + if (link->trace_state != NULL && link->trace_state[0] != '\0') { + if (ctr_link_set_trace_state(ctr_link, link->trace_state) != 0) { + return -1; + } + } ctr_link_set_dropped_attr_count(ctr_link, link->dropped_attributes_count); + ctr_link_set_flags(ctr_link, link->flags); } + return 0; +} + +static int decode_opentelemetry_error( + Opentelemetry__Proto__Collector__Trace__V1__ExportTraceServiceRequest *request, + struct ctrace *ctr, int result) +{ + opentelemetry__proto__collector__trace__v1__export_trace_service_request__free_unpacked( + request, NULL); + ctr_destroy(ctr); + + return result; } int ctr_decode_opentelemetry_create(struct ctrace **out_ctr, @@ -611,7 +632,13 @@ int ctr_decode_opentelemetry_create(struct ctrace **out_ctr, Opentelemetry__Proto__Trace__V1__ScopeSpans *otel_scope_span; Opentelemetry__Proto__Trace__V1__Span *otel_span; - if (*offset >= in_size) { + if (out_ctr == NULL || in_buf == NULL || offset == NULL) { + return CTR_DECODE_OPENTELEMETRY_INVALID_ARGUMENT; + } + + *out_ctr = NULL; + + if (*offset > in_size || (*offset == in_size && in_size != 0)) { return CTR_DECODE_OPENTELEMETRY_INSUFFICIENT_DATA; } @@ -644,11 +671,18 @@ int ctr_decode_opentelemetry_create(struct ctrace **out_ctr, /* resource span */ resource_span = ctr_resource_span_create(ctr); + if (resource_span == NULL) { + return decode_opentelemetry_error(service_request, ctr, + CTR_DECODE_OPENTELEMETRY_ALLOCATION_ERROR); + } ctr_resource_span_set_schema_url(resource_span, otel_resource_span->schema_url); /* resource */ resource = ctr_resource_span_get_resource(resource_span); - resource_set_data(resource, otel_resource_span->resource); + if (resource_set_data(resource, otel_resource_span->resource) != 0) { + return decode_opentelemetry_error(service_request, ctr, + CTR_DECODE_OPENTELEMETRY_ALLOCATION_ERROR); + } ctr_resource_set_dropped_attr_count(resource, otel_resource_span->resource->dropped_attributes_count); @@ -680,7 +714,10 @@ int ctr_decode_opentelemetry_create(struct ctrace **out_ctr, ctr_scope_span_set_schema_url(scope_span, otel_scope_span->schema_url); if (otel_scope_span->scope != NULL) { - ctr_scope_span_set_scope(scope_span, otel_scope_span->scope); + if (ctr_scope_span_set_scope(scope_span, otel_scope_span->scope) != 0) { + return decode_opentelemetry_error( + service_request, ctr, CTR_DECODE_OPENTELEMETRY_ALLOCATION_ERROR); + } } if (otel_scope_span->n_spans > 0 && otel_scope_span->spans == NULL) { @@ -709,30 +746,54 @@ int ctr_decode_opentelemetry_create(struct ctrace **out_ctr, } /* copy data from otel span to ctraces span representation */ - ctr_span_set_trace_id(span, otel_span->trace_id.data, otel_span->trace_id.len); - ctr_span_set_span_id(span, otel_span->span_id.data, otel_span->span_id.len); - ctr_span_set_parent_span_id(span, otel_span->parent_span_id.data, otel_span->parent_span_id.len); + if ((otel_span->trace_id.len > 0 && + ctr_span_set_trace_id(span, otel_span->trace_id.data, + otel_span->trace_id.len) != 0) || + (otel_span->span_id.len > 0 && + ctr_span_set_span_id(span, otel_span->span_id.data, + otel_span->span_id.len) != 0) || + (otel_span->parent_span_id.len > 0 && + ctr_span_set_parent_span_id(span, otel_span->parent_span_id.data, + otel_span->parent_span_id.len) != 0)) { + return decode_opentelemetry_error( + service_request, ctr, CTR_DECODE_OPENTELEMETRY_ALLOCATION_ERROR); + } if (otel_span->trace_state && strlen(otel_span->trace_state) > 0) { ctr_span_set_trace_state(span, otel_span->trace_state, strlen(otel_span->trace_state)); } - ctr_span_kind_set(span, otel_span->kind); + if (ctr_span_kind_set(span, otel_span->kind) != 0) { + return decode_opentelemetry_error( + service_request, ctr, CTR_DECODE_OPENTELEMETRY_INVALID_PAYLOAD); + } + ctr_span_set_flags(span, otel_span->flags); ctr_span_start_ts(ctr, span, otel_span->start_time_unix_nano); ctr_span_end_ts(ctr, span, otel_span->end_time_unix_nano); if (otel_span->status) { - ctr_span_set_status(span, otel_span->status->code, otel_span->status->message); + if (ctr_span_set_status(span, otel_span->status->code, + otel_span->status->message) != 0) { + return decode_opentelemetry_error( + service_request, ctr, CTR_DECODE_OPENTELEMETRY_INVALID_PAYLOAD); + } } - span_set_attributes(span, otel_span->n_attributes, otel_span->attributes); - span_set_events(span, otel_span->n_events, otel_span->events); + if (span_set_attributes(span, otel_span->n_attributes, + otel_span->attributes) != 0 || + span_set_events(span, otel_span->n_events, otel_span->events) != 0) { + return decode_opentelemetry_error( + service_request, ctr, CTR_DECODE_OPENTELEMETRY_INVALID_PAYLOAD); + } ctr_span_set_dropped_attributes_count(span, otel_span->dropped_attributes_count); ctr_span_set_dropped_events_count(span, otel_span->dropped_events_count); ctr_span_set_dropped_links_count(span, otel_span->dropped_links_count); - ctr_span_set_links(span, otel_span->n_links, otel_span->links); + if (ctr_span_set_links(span, otel_span->n_links, otel_span->links) != 0) { + return decode_opentelemetry_error( + service_request, ctr, CTR_DECODE_OPENTELEMETRY_INVALID_PAYLOAD); + } } } } diff --git a/src/ctr_encode_msgpack.c b/src/ctr_encode_msgpack.c index fadfadb..b20b3b0 100644 --- a/src/ctr_encode_msgpack.c +++ b/src/ctr_encode_msgpack.c @@ -42,6 +42,11 @@ static void pack_int64(mpack_writer_t *writer, int64_t val) mpack_write_i64(writer, val); } +static void pack_uint64(mpack_writer_t *writer, uint64_t val) +{ + mpack_write_u64(writer, val); +} + static void pack_double(mpack_writer_t *writer, double val) { mpack_write_double(writer, val); @@ -109,6 +114,9 @@ static void pack_variant(mpack_writer_t *writer, struct cfl_variant *variant) else if (type == CFL_VARIANT_INT) { pack_int64(writer, variant->data.as_int64); } + else if (type == CFL_VARIANT_UINT) { + pack_uint64(writer, variant->data.as_uint64); + } else if (type == CFL_VARIANT_DOUBLE) { pack_double(writer, variant->data.as_double); } @@ -121,8 +129,14 @@ static void pack_variant(mpack_writer_t *writer, struct cfl_variant *variant) else if (type == CFL_VARIANT_BYTES) { pack_bytes(writer, variant->data.as_bytes); } + else if (type == CFL_VARIANT_NULL) { + mpack_write_nil(writer); + } else if (type == CFL_VARIANT_REFERENCE) { - /* unsupported */ + mpack_writer_flag_error(writer, mpack_error_invalid); + } + else { + mpack_writer_flag_error(writer, mpack_error_invalid); } } @@ -266,7 +280,7 @@ static void pack_links(mpack_writer_t *writer, struct cfl_list *links) link = cfl_list_entry(head, struct ctrace_link, _head); /* start map */ - mpack_start_map(writer, 5); + mpack_start_map(writer, 6); /* trace_id */ mpack_write_cstr(writer, "trace_id"); @@ -298,6 +312,10 @@ static void pack_links(mpack_writer_t *writer, struct cfl_list *links) mpack_write_cstr(writer, "dropped_attributes_count"); mpack_write_u32(writer, link->dropped_attr_count); + /* flags */ + mpack_write_cstr(writer, "flags"); + mpack_write_u32(writer, link->flags); + /* end map */ mpack_finish_map(writer); } @@ -307,7 +325,7 @@ static void pack_links(mpack_writer_t *writer, struct cfl_list *links) static void pack_span(mpack_writer_t *writer, struct ctrace_span *span) { - mpack_start_map(writer, 16); + mpack_start_map(writer, 17); /* trace_id */ mpack_write_cstr(writer, "trace_id"); @@ -330,6 +348,10 @@ static void pack_span(mpack_writer_t *writer, struct ctrace_span *span) mpack_write_nil(writer); } + /* flags */ + mpack_write_cstr(writer, "flags"); + mpack_write_u32(writer, span->flags); + /* name */ mpack_write_cstr(writer, "name"); if (span->name) { @@ -477,10 +499,13 @@ int ctr_encode_msgpack_create(struct ctrace *ctx, char **out_buf, size_t *out_s struct ctrace_resource_span *resource_span; struct ctrace_resource *resource; - if (ctx == NULL) { + if (ctx == NULL || out_buf == NULL || out_size == NULL) { return -1; } + *out_buf = NULL; + *out_size = 0; + mpack_writer_init_growable(&writer, &data, &size); /* root map */ diff --git a/src/ctr_encode_opentelemetry.c b/src/ctr_encode_opentelemetry.c index 28373b3..e56770f 100644 --- a/src/ctr_encode_opentelemetry.c +++ b/src/ctr_encode_opentelemetry.c @@ -35,6 +35,16 @@ static inline void otlp_array_destroy(Opentelemetry__Proto__Common__V1__ArrayVal static inline void otlp_kvpair_list_destroy(Opentelemetry__Proto__Common__V1__KeyValue **pair_list, size_t entry_count); static void destroy_spans(Opentelemetry__Proto__Trace__V1__Span **spans, size_t count); +static void destroy_span(Opentelemetry__Proto__Trace__V1__Span *span); +static void destroy_event(Opentelemetry__Proto__Trace__V1__Span__Event *event); +static void destroy_link(Opentelemetry__Proto__Trace__V1__Span__Link *link); +static void destroy_events(Opentelemetry__Proto__Trace__V1__Span__Event **events, size_t count); +static void destroy_links(Opentelemetry__Proto__Trace__V1__Span__Link **links, size_t count); +static void destroy_scope(Opentelemetry__Proto__Common__V1__InstrumentationScope *scope); +static void destroy_scope_span(Opentelemetry__Proto__Trace__V1__ScopeSpans *scope_span); +static void destroy_resource(Opentelemetry__Proto__Resource__V1__Resource *resource); +static void destroy_resource_spans(Opentelemetry__Proto__Trace__V1__ResourceSpans **rs, + int resource_span_count); static inline void otlp_kvpair_destroy(Opentelemetry__Proto__Common__V1__KeyValue *kvpair) { @@ -221,6 +231,9 @@ static Opentelemetry__Proto__Common__V1__AnyValue *otlp_any_value_initialize(int else if (data_type == CFL_VARIANT_INT) { value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_INT_VALUE; } + else if (data_type == CFL_VARIANT_UINT) { + value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_INT_VALUE; + } else if (data_type == CFL_VARIANT_DOUBLE) { value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_DOUBLE_VALUE; } @@ -249,9 +262,6 @@ static Opentelemetry__Proto__Common__V1__AnyValue *otlp_any_value_initialize(int else if (data_type == CFL_VARIANT_BYTES) { value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE; } - else if (data_type == CFL_VARIANT_REFERENCE) { - value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE; - } else { free(value); @@ -481,14 +491,17 @@ static inline Opentelemetry__Proto__Common__V1__AnyValue *ctr_variant_binary_to_ if (result != NULL) { result->bytes_value.len = cfl_sds_len(value->data.as_bytes); - result->bytes_value.data = calloc(result->bytes_value.len, sizeof(char)); + result->bytes_value.data = calloc(result->bytes_value.len + 1, sizeof(char)); if (result->bytes_value.data == NULL) { otlp_any_value_destroy(result); return NULL; } - memcpy(result->bytes_value.data, value->data.as_bytes, result->bytes_value.len); + if (result->bytes_value.len > 0) { + memcpy(result->bytes_value.data, value->data.as_bytes, + result->bytes_value.len); + } } return result; @@ -507,6 +520,17 @@ static inline Opentelemetry__Proto__Common__V1__AnyValue *ctr_variant_to_otlp_an else if (value->type == CFL_VARIANT_INT) { result = ctr_variant_int64_to_otlp_any_value(value); } + else if (value->type == CFL_VARIANT_UINT) { + if (value->data.as_uint64 > INT64_MAX) { + result = NULL; + } + else { + result = otlp_any_value_initialize(CFL_VARIANT_UINT, 0); + if (result != NULL) { + result->int_value = (int64_t) value->data.as_uint64; + } + } + } else if (value->type == CFL_VARIANT_DOUBLE) { result = ctr_variant_double_to_otlp_any_value(value); } @@ -519,9 +543,6 @@ static inline Opentelemetry__Proto__Common__V1__AnyValue *ctr_variant_to_otlp_an else if (value->type == CFL_VARIANT_BYTES) { result = ctr_variant_binary_to_otlp_any_value(value); } - else if (value->type == CFL_VARIANT_REFERENCE) { - result = ctr_variant_string_to_otlp_any_value(value); - } else { result = NULL; } @@ -574,6 +595,10 @@ static Opentelemetry__Proto__Resource__V1__Resource *ctr_set_resource(struct ctr otel_resource->n_attributes = get_attributes_count(resource->attr); otel_resource->attributes = set_attributes_from_ctr(resource->attr); + if (otel_resource->n_attributes > 0 && otel_resource->attributes == NULL) { + destroy_resource(otel_resource); + return NULL; + } otel_resource->dropped_attributes_count = resource->dropped_attr_count; return otel_resource; @@ -695,6 +720,10 @@ static Opentelemetry__Proto__Trace__V1__Span__Event *set_event(struct ctrace_spa event->name = ctr_event->name; event->n_attributes = ctr_attributes_count(ctr_event->attr); event->attributes = set_attributes_from_ctr(ctr_event->attr); + if (event->n_attributes > 0 && event->attributes == NULL) { + destroy_event(event); + return NULL; + } event->dropped_attributes_count = ctr_event->dropped_attr_count; return event; @@ -709,6 +738,10 @@ static Opentelemetry__Proto__Trace__V1__Span__Event **set_events_from_ctr(struct count = cfl_list_size(events); + if (count == 0) { + return NULL; + } + Opentelemetry__Proto__Trace__V1__Span__Event **event_arr; event_arr = calloc(count, sizeof(Opentelemetry__Proto__Trace__V1__Span__Event *)); @@ -720,17 +753,28 @@ static Opentelemetry__Proto__Trace__V1__Span__Event **set_events_from_ctr(struct event_index = 0; cfl_list_foreach(head, events) { ctr_event = cfl_list_entry(head, struct ctrace_span_event, _head); - event_arr[event_index++] = set_event(ctr_event); + event_arr[event_index] = set_event(ctr_event); + if (event_arr[event_index] == NULL) { + destroy_events(event_arr, event_index); + return NULL; + } + event_index++; } return event_arr; } -static void otel_span_set_events(Opentelemetry__Proto__Trace__V1__Span *otel_span, - struct cfl_list *events) +static int otel_span_set_events(Opentelemetry__Proto__Trace__V1__Span *otel_span, + struct cfl_list *events) { otel_span->n_events = cfl_list_size(events); otel_span->events = set_events_from_ctr(events); + + if (otel_span->n_events > 0 && otel_span->events == NULL) { + return -1; + } + + return 0; } static void otel_span_set_dropped_events_count(Opentelemetry__Proto__Trace__V1__Span *span, @@ -751,15 +795,15 @@ static void otel_span_set_trace_state(Opentelemetry__Proto__Trace__V1__Span *ote otel_span->trace_state = trace_state; } -static void otel_span_set_status(Opentelemetry__Proto__Trace__V1__Span *otel_span, - struct ctrace_span_status status) +static int otel_span_set_status(Opentelemetry__Proto__Trace__V1__Span *otel_span, + struct ctrace_span_status status) { Opentelemetry__Proto__Trace__V1__Status *otel_status; otel_status = calloc(1, sizeof(Opentelemetry__Proto__Trace__V1__Status)); if (!otel_status) { ctr_errno(); - return; + return -1; } opentelemetry__proto__trace__v1__status__init(otel_status); @@ -767,10 +811,12 @@ static void otel_span_set_status(Opentelemetry__Proto__Trace__V1__Span *otel_spa otel_status->message = status.message; otel_span->status = otel_status; + + return 0; } -static void otel_span_set_links(Opentelemetry__Proto__Trace__V1__Span *otel_span, - struct cfl_list *links) +static int otel_span_set_links(Opentelemetry__Proto__Trace__V1__Span *otel_span, + struct cfl_list *links) { int count; int link_index; @@ -783,13 +829,19 @@ static void otel_span_set_links(Opentelemetry__Proto__Trace__V1__Span *otel_span count = cfl_list_size(links); + if (count == 0) { + otel_span->n_links = 0; + otel_span->links = NULL; + return 0; + } + Opentelemetry__Proto__Trace__V1__Span__Link **otel_links; Opentelemetry__Proto__Trace__V1__Span__Link *otel_link; otel_links = calloc(count, sizeof(Opentelemetry__Proto__Trace__V1__Span__Link *)); if (!otel_links) { ctr_errno(); - return; + return -1; } link_index = 0; @@ -800,7 +852,8 @@ static void otel_span_set_links(Opentelemetry__Proto__Trace__V1__Span *otel_span otel_link = calloc(1, sizeof(Opentelemetry__Proto__Trace__V1__Span__Link)); if (!otel_link) { ctr_errno(); - break; + destroy_links(otel_links, link_index); + return -1; } opentelemetry__proto__trace__v1__span__link__init(otel_link); @@ -824,17 +877,25 @@ static void otel_span_set_links(Opentelemetry__Proto__Trace__V1__Span *otel_span otel_link->n_attributes = get_attributes_count(link->attr); otel_link->attributes = set_attributes_from_ctr(link->attr); + if (otel_link->n_attributes > 0 && otel_link->attributes == NULL) { + destroy_link(otel_link); + destroy_links(otel_links, link_index); + return -1; + } otel_link->dropped_attributes_count = link->dropped_attr_count; + otel_link->flags = link->flags; otel_links[link_index++] = otel_link; } otel_span->n_links = link_index; otel_span->links = otel_links; + + return 0; } -static void set_span(Opentelemetry__Proto__Trace__V1__Span *otel_span, - struct ctrace_span *span) +static int set_span(Opentelemetry__Proto__Trace__V1__Span *otel_span, + struct ctrace_span *span) { otel_span_set_name(otel_span, span->name); otel_span_set_trace_id(otel_span, span->trace_id); @@ -842,15 +903,28 @@ static void set_span(Opentelemetry__Proto__Trace__V1__Span *otel_span, otel_span_set_parent_span_id(otel_span, span->parent_span_id); otel_span_set_kind(otel_span, span->kind); otel_span_set_trace_state(otel_span, span->trace_state); + otel_span->flags = span->flags; otel_span_set_start_time(otel_span, span->start_time_unix_nano); otel_span_set_end_time(otel_span, span->end_time_unix_nano); - otel_span_set_status(otel_span, span->status); + if (otel_span_set_status(otel_span, span->status) != 0) { + return -1; + } otel_span_set_attributes(otel_span, span->attr); + if (otel_span->n_attributes > 0 && otel_span->attributes == NULL) { + return -1; + } otel_span_set_dropped_attributes_count(otel_span, span->dropped_attr_count); - otel_span_set_events(otel_span, &span->events); + if (otel_span_set_events(otel_span, &span->events) != 0) { + return -1; + } otel_span_set_dropped_events_count(otel_span, span->dropped_events_count); - otel_span_set_links(otel_span, &span->links); + if (otel_span_set_links(otel_span, &span->links) != 0) { + return -1; + } + otel_span->dropped_links_count = span->dropped_links_count; + + return 0; } static Opentelemetry__Proto__Trace__V1__Span **initialize_spans(size_t span_count) @@ -892,6 +966,9 @@ static Opentelemetry__Proto__Trace__V1__Span **set_spans(struct ctrace_scope_spa Opentelemetry__Proto__Trace__V1__Span *otel_span; span_count = cfl_list_size(&scope_span->spans); + if (span_count == 0) { + return NULL; + } spans = initialize_spans(span_count); if (!spans) { return NULL; @@ -904,13 +981,15 @@ static Opentelemetry__Proto__Trace__V1__Span **set_spans(struct ctrace_scope_spa otel_span = initialize_span(); if (!otel_span) { - if (span_index > 0) { - destroy_spans(spans, span_index); - } + destroy_spans(spans, span_index); return NULL; } - set_span(otel_span, span); + if (set_span(otel_span, span) != 0) { + destroy_span(otel_span); + destroy_spans(spans, span_index); + return NULL; + } spans[span_index++] = otel_span; } @@ -955,6 +1034,10 @@ static Opentelemetry__Proto__Common__V1__InstrumentationScope *set_instrumentati otel_scope->n_attributes = get_attributes_count(instrumentation_scope->attr); otel_scope->dropped_attributes_count = instrumentation_scope->dropped_attr_count; otel_scope->attributes = set_attributes_from_ctr(instrumentation_scope->attr); + if (otel_scope->n_attributes > 0 && otel_scope->attributes == NULL) { + destroy_scope(otel_scope); + return NULL; + } return otel_scope; } @@ -1000,6 +1083,9 @@ static Opentelemetry__Proto__Trace__V1__ScopeSpans **set_scope_spans(struct ctra scope_span_count = cfl_list_size(&resource_span->scope_spans); + if (scope_span_count == 0) { + return NULL; + } scope_spans = initialize_scope_spans(scope_span_count); if (!scope_spans) { return NULL; @@ -1012,21 +1098,28 @@ static Opentelemetry__Proto__Trace__V1__ScopeSpans **set_scope_spans(struct ctra otel_scope_span = initialize_scope_span(); if (!otel_scope_span) { - if (scope_span_index > 0) { - destroy_scope_spans(scope_spans, scope_span_index - 1); - } - /* note: scope_spans is freed inside destroy_scope_spans() */ + destroy_scope_spans(scope_spans, scope_span_index); return NULL; } otel_scope_span->schema_url = scope_span->schema_url; if (scope_span->instrumentation_scope != NULL) { otel_scope_span->scope = set_instrumentation_scope(scope_span->instrumentation_scope); + if (otel_scope_span->scope == NULL) { + destroy_scope_span(otel_scope_span); + destroy_scope_spans(scope_spans, scope_span_index); + return NULL; + } } span_count = cfl_list_size(&scope_span->spans); otel_scope_span->n_spans = span_count; otel_scope_span->spans = set_spans(scope_span); + if (span_count > 0 && otel_scope_span->spans == NULL) { + destroy_scope_span(otel_scope_span); + destroy_scope_spans(scope_spans, scope_span_index); + return NULL; + } scope_spans[scope_span_index++] = otel_scope_span; } @@ -1075,6 +1168,9 @@ static Opentelemetry__Proto__Trace__V1__ResourceSpans **set_resource_spans(struc Opentelemetry__Proto__Trace__V1__ScopeSpans **scope_spans; resource_span_count = cfl_list_size(&ctr->resource_spans); + if (resource_span_count == 0) { + return NULL; + } rs = initialize_resource_spans(resource_span_count); if (!rs) { return NULL; @@ -1087,13 +1183,24 @@ static Opentelemetry__Proto__Trace__V1__ResourceSpans **set_resource_spans(struc otel_resource_span = initialize_resource_span(); if (!otel_resource_span) { - free(rs); + destroy_resource_spans(rs, resource_span_index); return NULL; } otel_resource_span->resource = ctr_set_resource(resource_span->resource); + if (otel_resource_span->resource == NULL) { + free(otel_resource_span); + destroy_resource_spans(rs, resource_span_index); + return NULL; + } otel_resource_span->n_scope_spans = cfl_list_size(&resource_span->scope_spans); scope_spans = set_scope_spans(resource_span); + if (otel_resource_span->n_scope_spans > 0 && scope_spans == NULL) { + destroy_resource(otel_resource_span->resource); + free(otel_resource_span); + destroy_resource_spans(rs, resource_span_index); + return NULL; + } otel_resource_span->scope_spans = scope_spans; otel_resource_span->schema_url = resource_span->schema_url; @@ -1128,12 +1235,12 @@ static Opentelemetry__Proto__Collector__Trace__V1__ExportTraceServiceRequest *cr return NULL; } + req->n_resource_spans = cfl_list_size(&ctr->resource_spans); rs = set_resource_spans(ctr); - if (!rs) { + if (req->n_resource_spans > 0 && !rs) { free(req); return NULL; } - req->n_resource_spans = cfl_list_size(&ctr->resource_spans); req->resource_spans = rs; return req; @@ -1272,6 +1379,10 @@ static void destroy_spans(Opentelemetry__Proto__Trace__V1__Span **spans, size_t { int span_index; + if (spans == NULL) { + return; + } + for (span_index = 0; span_index < count; span_index++) { destroy_span(spans[span_index]); } @@ -1364,6 +1475,10 @@ cfl_sds_t ctr_encode_opentelemetry_create(struct ctrace *ctr) size_t len; Opentelemetry__Proto__Collector__Trace__V1__ExportTraceServiceRequest *req; + if (ctr == NULL) { + return NULL; + } + req = create_export_service_request(ctr); if (!req) { return NULL; diff --git a/src/ctr_encode_text.c b/src/ctr_encode_text.c index f2ff1e5..b81e3e5 100644 --- a/src/ctr_encode_text.c +++ b/src/ctr_encode_text.c @@ -27,19 +27,37 @@ static inline void sds_cat_safe(cfl_sds_t *buf, char *str) cfl_sds_cat_safe(buf, str, len); } +static inline char *string_or_empty(char *str) +{ + if (str == NULL) { + return ""; + } + + return str; +} + static void format_string(cfl_sds_t *buf, cfl_sds_t val, int level) +{ + sds_cat_safe(buf, "'"); + if (val != NULL) { + cfl_sds_cat_safe(buf, val, cfl_sds_len(val)); + } + sds_cat_safe(buf, "'"); +} + +static void format_int64(cfl_sds_t *buf, int64_t val, int level) { char tmp[1024]; - snprintf(tmp, sizeof(tmp) - 1, "'%s'", val); + snprintf(tmp, sizeof(tmp) - 1, "%" PRIi64, val); sds_cat_safe(buf, tmp); } -static void format_int64(cfl_sds_t *buf, int64_t val, int level) +static void format_uint64(cfl_sds_t *buf, uint64_t val, int level) { char tmp[1024]; - snprintf(tmp, sizeof(tmp) - 1, "%" PRIi64, val); + snprintf(tmp, sizeof(tmp) - 1, "%" PRIu64, val); sds_cat_safe(buf, tmp); } @@ -86,6 +104,12 @@ static void format_array(cfl_sds_t *buf, struct cfl_array *array, int level) else if (v->type == CFL_VARIANT_INT) { format_int64(buf, v->data.as_int64, off); } + else if (v->type == CFL_VARIANT_UINT) { + format_uint64(buf, v->data.as_uint64, off); + } + else if (v->type == CFL_VARIANT_NULL) { + sds_cat_safe(buf, "null"); + } else if (v->type == CFL_VARIANT_DOUBLE) { format_double(buf, v->data.as_double, off); } @@ -131,6 +155,12 @@ static void format_attributes(cfl_sds_t *buf, struct cfl_kvlist *kv, int level) else if (v->type == CFL_VARIANT_INT) { format_int64(buf, v->data.as_int64, off); } + else if (v->type == CFL_VARIANT_UINT) { + format_uint64(buf, v->data.as_uint64, off); + } + else if (v->type == CFL_VARIANT_NULL) { + sds_cat_safe(buf, "null"); + } else if (v->type == CFL_VARIANT_DOUBLE) { format_double(buf, v->data.as_double, off); } @@ -152,7 +182,8 @@ static void format_event(cfl_sds_t *buf, struct ctrace_span_event *event, int le sds_cat_safe(buf, "\n"); - snprintf(tmp, sizeof(tmp) - 1, "%*s- name: %s\n", off, "", event->name); + snprintf(tmp, sizeof(tmp) - 1, "%*s- name: %s\n", off, "", + string_or_empty(event->name)); sds_cat_safe(buf, tmp); off += 4; @@ -188,7 +219,8 @@ static void format_span(cfl_sds_t *buf, struct ctrace *ctx, int id, struct ctrac min = off + 4; - snprintf(tmp, sizeof(tmp) - 1, "%*s[span #%i '%s']\n", off, "", id, span->name); + snprintf(tmp, sizeof(tmp) - 1, "%*s[span #%i '%s']\n", off, "", id, + string_or_empty(span->name)); sds_cat_safe(buf, tmp); /* trace_id */ @@ -249,7 +281,8 @@ static void format_span(cfl_sds_t *buf, struct ctrace *ctx, int id, struct ctrac sds_cat_safe(buf, tmp); /* trace_state */ - snprintf(tmp, sizeof(tmp) - 1, "%*s- trace_state : %s\n", min, "", span->trace_state); + snprintf(tmp, sizeof(tmp) - 1, "%*s- trace_state : %s\n", min, "", + string_or_empty(span->trace_state)); sds_cat_safe(buf, tmp); /* schema_url */ @@ -331,7 +364,8 @@ static void format_span(cfl_sds_t *buf, struct ctrace *ctx, int id, struct ctrac sds_cat_safe(buf, tmp); cfl_sds_destroy(id_hex); - snprintf(tmp, sizeof(tmp) - 1, "%*s- trace_state : %s\n", off, "", link->trace_state); + snprintf(tmp, sizeof(tmp) - 1, "%*s- trace_state : %s\n", off, "", + string_or_empty(link->trace_state)); sds_cat_safe(buf, tmp); snprintf(tmp, sizeof(tmp) - 1, "%*s- dropped_events_count : %" PRIu32 "\n", off, "", link->dropped_attr_count); @@ -372,8 +406,10 @@ static void format_instrumentation_scope(cfl_sds_t *buf, struct ctrace_instrumentation_scope *scope) { cfl_sds_printf(buf, " instrumentation scope:\n"); - cfl_sds_printf(buf, " - name : %s\n", scope->name); - cfl_sds_printf(buf, " - version : %s\n", scope->version); + cfl_sds_printf(buf, " - name : %s\n", + string_or_empty(scope->name)); + cfl_sds_printf(buf, " - version : %s\n", + string_or_empty(scope->version)); cfl_sds_printf(buf, " - dropped_attributes_count: %i\n", scope->dropped_attr_count); if (scope->attr) { diff --git a/src/ctr_mpack_utils.c b/src/ctr_mpack_utils.c index 222e266..536ffca 100644 --- a/src/ctr_mpack_utils.c +++ b/src/ctr_mpack_utils.c @@ -20,6 +20,7 @@ #include #include #include +#include int ctr_mpack_consume_string_or_nil_tag(mpack_reader_t *reader, cfl_sds_t *output_buffer) { @@ -126,6 +127,9 @@ int ctr_mpack_consume_uint_tag(mpack_reader_t *reader, uint64_t *output_buffer) } if (mpack_type_int == mpack_tag_type(&tag)) { + if (mpack_tag_int_value(&tag) < 0) { + return CTR_MPACK_CORRUPT_INPUT_DATA_ERROR; + } *output_buffer = (uint64_t) mpack_tag_int_value(&tag); } else if (mpack_type_uint == mpack_tag_type(&tag)) { @@ -146,6 +150,9 @@ int ctr_mpack_consume_uint32_tag(mpack_reader_t *reader, uint32_t *output_buffer result = ctr_mpack_consume_uint_tag(reader, &value); if (result == CTR_MPACK_SUCCESS) { + if (value > UINT32_MAX) { + return CTR_MPACK_CORRUPT_INPUT_DATA_ERROR; + } *output_buffer = (uint32_t) value; } @@ -179,6 +186,9 @@ int ctr_mpack_consume_int_tag(mpack_reader_t *reader, int64_t *output_buffer) *output_buffer = (int64_t) mpack_tag_int_value(&tag); } else if (mpack_type_uint == mpack_tag_type(&tag)) { + if (mpack_tag_uint_value(&tag) > INT64_MAX) { + return CTR_MPACK_CORRUPT_INPUT_DATA_ERROR; + } *output_buffer = (int64_t) mpack_tag_uint_value(&tag); } else { @@ -196,6 +206,9 @@ int ctr_mpack_consume_int32_tag(mpack_reader_t *reader, int32_t *output_buffer) result = ctr_mpack_consume_int_tag(reader, &value); if (result == CTR_MPACK_SUCCESS) { + if (value < INT32_MIN || value > INT32_MAX) { + return CTR_MPACK_CORRUPT_INPUT_DATA_ERROR; + } *output_buffer = (int32_t) value; } @@ -335,6 +348,8 @@ int ctr_mpack_unpack_map(mpack_reader_t *reader, struct ctr_mpack_map_entry_callback_t *callback_entry; uint32_t entry_index; uint32_t entry_count; + uint32_t callback_index; + uint8_t handled_entries[CTR_MPACK_MAX_MAP_ENTRY_COUNT]; cfl_sds_t key_name; int result; mpack_tag_t tag; @@ -363,22 +378,31 @@ int ctr_mpack_unpack_map(mpack_reader_t *reader, } result = 0; + memset(handled_entries, 0, sizeof(handled_entries)); for (entry_index = 0 ; 0 == result && entry_index < entry_count ; entry_index++) { result = ctr_mpack_consume_string_tag(reader, &key_name); if (CTR_MPACK_SUCCESS == result) { callback_entry = callback_list; + callback_index = 0; result = CTR_MPACK_UNEXPECTED_KEY_ERROR; while (CTR_MPACK_UNEXPECTED_KEY_ERROR == result && NULL != callback_entry->identifier) { if (0 == strcmp(callback_entry->identifier, key_name)) { - result = callback_entry->handler(reader, entry_index, context); + if (handled_entries[callback_index]) { + result = CTR_MPACK_CORRUPT_INPUT_DATA_ERROR; + } + else { + handled_entries[callback_index] = 1; + result = callback_entry->handler(reader, entry_index, context); + } } callback_entry++; + callback_index++; } cfl_sds_destroy(key_name); diff --git a/src/ctr_resource.c b/src/ctr_resource.c index b2db669..a2609f9 100644 --- a/src/ctr_resource.c +++ b/src/ctr_resource.c @@ -105,6 +105,7 @@ struct ctrace_resource_span *ctr_resource_span_create(struct ctrace *ctx) return NULL; } cfl_list_init(&resource_span->scope_spans); + resource_span->ctx = ctx; /* create an empty resource */ resource_span->resource = ctr_resource_create(); diff --git a/src/ctr_scope.c b/src/ctr_scope.c index c41297c..52819b7 100644 --- a/src/ctr_scope.c +++ b/src/ctr_scope.c @@ -90,11 +90,12 @@ void ctr_scope_span_set_instrumentation_scope(struct ctrace_scope_span *scope_sp struct ctrace_instrumentation_scope *scope) { /* Safeguard against leaks */ - if (scope_span->instrumentation_scope != NULL) { - ctr_instrumentation_scope_destroy(scope_span->instrumentation_scope); + if (scope_span->instrumentation_scope != scope) { + if (scope_span->instrumentation_scope != NULL) { + ctr_instrumentation_scope_destroy(scope_span->instrumentation_scope); + } + scope_span->instrumentation_scope = scope; } - - scope_span->instrumentation_scope = scope; } struct ctrace_instrumentation_scope *ctr_instrumentation_scope_create(char *name, char *version, diff --git a/src/ctr_span.c b/src/ctr_span.c index 5d309b0..1437beb 100644 --- a/src/ctr_span.c +++ b/src/ctr_span.c @@ -28,7 +28,8 @@ struct ctrace_span *ctr_span_create(struct ctrace *ctx, struct ctrace_scope_span { struct ctrace_span *span; - if (!ctx || !scope_span || !name) { + if (!ctx || !scope_span || !name || !scope_span->resource_span || + scope_span->resource_span->ctx != ctx) { return NULL; } @@ -178,6 +179,10 @@ int ctr_span_set_parent_span_id_with_cid(struct ctrace_span *span, struct ctrace int ctr_span_kind_set(struct ctrace_span *span, int kind) { + if (span == NULL) { + return -1; + } + if (kind < CTRACE_SPAN_UNSPECIFIED || kind > CTRACE_SPAN_CONSUMER) { return -1; } @@ -215,11 +220,10 @@ int ctr_span_set_attributes(struct ctrace_span *span, struct ctrace_attributes * return -1; } - if (span->attr) { + if (span->attr != attr) { ctr_attributes_destroy(span->attr); + span->attr = attr; } - - span->attr = attr; return 0; } @@ -291,6 +295,11 @@ int ctr_span_set_status(struct ctrace_span *span, int code, char *message) cfl_sds_t new_message; struct ctrace_span_status *status; + if (span == NULL || code < CTRACE_SPAN_STATUS_CODE_UNSET || + code > CTRACE_SPAN_STATUS_CODE_ERROR) { + return -1; + } + new_message = NULL; if (message) { new_message = cfl_sds_create(message); @@ -499,6 +508,11 @@ int ctr_span_event_set_attribute_int64(struct ctrace_span_event *event, char *ke return ctr_attributes_set_int64(event->attr, key, value); } +int ctr_span_event_set_attribute_int(struct ctrace_span_event *event, char *key, int value) +{ + return ctr_span_event_set_attribute_int64(event, key, value); +} + int ctr_span_event_set_attribute_double(struct ctrace_span_event *event, char *key, double value) { return ctr_attributes_set_double(event->attr, key, value); @@ -523,11 +537,10 @@ int ctr_span_event_set_attributes(struct ctrace_span_event *event, struct ctrace return -1; } - if (event->attr) { + if (event->attr != attr) { ctr_attributes_destroy(event->attr); + event->attr = attr; } - - event->attr = attr; return 0; } diff --git a/tests/decoding.c b/tests/decoding.c index 4e4a9a4..e21a4ff 100644 --- a/tests/decoding.c +++ b/tests/decoding.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include "ctr_tests.h" @@ -534,7 +536,7 @@ static void msgpack_encode_decode_and_compare(struct ctrace *context) result = ctr_decode_msgpack_create(&decoded_context, msgpack_text_buffer, msgpack_text_size, &offset); TEST_ASSERT(result == 0); - validation_text_buffer = ctr_encode_text_create(context); + validation_text_buffer = ctr_encode_text_create(decoded_context); TEST_ASSERT(validation_text_buffer != NULL); TEST_ASSERT(strcmp(referece_text_buffer, validation_text_buffer) == 0); @@ -576,6 +578,243 @@ void test_msgpack_to_ctr_with_empty_spans() ctr_destroy(context); } +void test_msgpack_preserves_flags() +{ + char *buffer; + size_t size; + size_t offset; + struct ctrace *ctx; + struct ctrace *decoded; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + struct ctrace_span *span; + struct ctrace_link *link; + int result; + + ctx = ctr_create(NULL); + rs = ctr_resource_span_create(ctx); + ss = ctr_scope_span_create(rs); + span = ctr_span_create(ctx, ss, "flags", NULL); + link = ctr_link_create(span, NULL, 0, NULL, 0); + TEST_ASSERT(link != NULL); + + ctr_span_set_flags(span, 0x301); + ctr_span_set_dropped_links_count(span, 17); + ctr_link_set_flags(link, 0x201); + + result = ctr_encode_msgpack_create(ctx, &buffer, &size); + TEST_ASSERT(result == 0); + + offset = 0; + result = ctr_decode_msgpack_create(&decoded, buffer, size, &offset); + TEST_ASSERT(result == 0); + TEST_ASSERT(decoded != NULL); + + rs = cfl_list_entry(decoded->resource_spans.next, + struct ctrace_resource_span, _head); + ss = cfl_list_entry(rs->scope_spans.next, struct ctrace_scope_span, _head); + span = cfl_list_entry(ss->spans.next, struct ctrace_span, _head); + link = cfl_list_entry(span->links.next, struct ctrace_link, _head); + + TEST_CHECK(span->flags == 0x301); + TEST_CHECK(span->dropped_links_count == 17); + TEST_CHECK(link->flags == 0x201); + + ctr_destroy(decoded); + ctr_encode_msgpack_destroy(buffer); + ctr_destroy(ctx); +} + +void test_msgpack_invalid_offset() +{ + char data[] = {0x80}; + size_t offset; + struct ctrace *decoded; + int result; + + offset = sizeof(data) + 1; + decoded = (struct ctrace *) 0x1; + result = ctr_decode_msgpack_create(&decoded, data, sizeof(data), &offset); + TEST_CHECK(result == CTR_DECODE_MSGPACK_INSUFFICIENT_DATA); + TEST_CHECK(decoded == NULL); +} + +void test_msgpack_integer_ranges() +{ + char negative[] = {(char) 0xff}; + char overflow_u32[] = {(char) 0xcf, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00}; + char overflow_i32[] = {(char) 0xce, (char) 0x80, 0x00, 0x00, 0x00}; + mpack_reader_t reader; + uint64_t u64; + uint32_t u32; + int32_t i32; + + mpack_reader_init_data(&reader, negative, sizeof(negative)); + TEST_CHECK(ctr_mpack_consume_uint64_tag(&reader, &u64) == + CTR_MPACK_CORRUPT_INPUT_DATA_ERROR); + mpack_reader_destroy(&reader); + + mpack_reader_init_data(&reader, overflow_u32, sizeof(overflow_u32)); + TEST_CHECK(ctr_mpack_consume_uint32_tag(&reader, &u32) == + CTR_MPACK_CORRUPT_INPUT_DATA_ERROR); + mpack_reader_destroy(&reader); + + mpack_reader_init_data(&reader, overflow_i32, sizeof(overflow_i32)); + TEST_CHECK(ctr_mpack_consume_int32_tag(&reader, &i32) == + CTR_MPACK_CORRUPT_INPUT_DATA_ERROR); + mpack_reader_destroy(&reader); +} + +void test_msgpack_long_attribute_key() +{ + char key[512]; + char *buffer; + size_t size; + size_t offset; + struct ctrace *ctx; + struct ctrace *decoded; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + struct ctrace_span *span; + struct cfl_variant *null_value; + struct cfl_variant *value; + int result; + + memset(key, 'k', sizeof(key) - 1); + key[sizeof(key) - 1] = '\0'; + + ctx = ctr_create(NULL); + rs = ctr_resource_span_create(ctx); + ss = ctr_scope_span_create(rs); + span = ctr_span_create(ctx, ss, "long-key", NULL); + TEST_ASSERT(ctr_span_set_attribute_string(span, key, "value") == 0); + TEST_ASSERT(cfl_kvlist_insert_uint64(span->attr->kv, "uint", UINT64_MAX) == 0); + null_value = cfl_variant_create_from_null(); + TEST_ASSERT(null_value != NULL); + TEST_ASSERT(cfl_kvlist_insert(span->attr->kv, "null", null_value) == 0); + TEST_ASSERT(ctr_encode_msgpack_create(ctx, &buffer, &size) == 0); + + offset = 0; + result = ctr_decode_msgpack_create(&decoded, buffer, size, &offset); + TEST_ASSERT(result == 0); + rs = cfl_list_entry(decoded->resource_spans.next, + struct ctrace_resource_span, _head); + ss = cfl_list_entry(rs->scope_spans.next, struct ctrace_scope_span, _head); + span = cfl_list_entry(ss->spans.next, struct ctrace_span, _head); + TEST_CHECK(cfl_kvlist_fetch(span->attr->kv, key) != NULL); + value = cfl_kvlist_fetch(span->attr->kv, "uint"); + TEST_ASSERT(value != NULL); + TEST_CHECK(value->type == CFL_VARIANT_UINT); + TEST_CHECK(value->data.as_uint64 == UINT64_MAX); + value = cfl_kvlist_fetch(span->attr->kv, "null"); + TEST_ASSERT(value != NULL); + TEST_CHECK(value->type == CFL_VARIANT_NULL); + + ctr_destroy(decoded); + ctr_encode_msgpack_destroy(buffer); + ctr_destroy(ctx); +} + +void test_msgpack_variant_limits() +{ + char *buffer; + size_t size; + int index; + int result; + mpack_writer_t writer; + mpack_reader_t reader; + struct cfl_variant *variant; + struct cfl_kvlist *kvlist; + + mpack_writer_init_growable(&writer, &buffer, &size); + for (index = 0; index < CFL_VARIANT_UTILS_MAXIMUM_NESTING_DEPTH + 1; index++) { + mpack_start_array(&writer, 1); + } + mpack_write_i64(&writer, 1); + for (index = 0; index < CFL_VARIANT_UTILS_MAXIMUM_NESTING_DEPTH + 1; index++) { + mpack_finish_array(&writer); + } + TEST_ASSERT(mpack_writer_destroy(&writer) == mpack_ok); + + mpack_reader_init_data(&reader, buffer, size); + result = unpack_cfl_variant(&reader, &variant); + TEST_CHECK(result != 0); + mpack_reader_destroy(&reader); + free(buffer); + + mpack_writer_init_growable(&writer, &buffer, &size); + mpack_start_map(&writer, 1); + mpack_write_cstr(&writer, "overflow"); + mpack_write_u64(&writer, UINT64_MAX); + mpack_finish_map(&writer); + TEST_ASSERT(mpack_writer_destroy(&writer) == mpack_ok); + + mpack_reader_init_data(&reader, buffer, size); + result = unpack_cfl_kvlist(&reader, &kvlist); + TEST_ASSERT(result == 0); + variant = cfl_kvlist_fetch(kvlist, "overflow"); + TEST_ASSERT(variant != NULL); + TEST_CHECK(variant->type == CFL_VARIANT_UINT); + TEST_CHECK(variant->data.as_uint64 == UINT64_MAX); + cfl_kvlist_destroy(kvlist); + mpack_reader_destroy(&reader); + free(buffer); +} + +void test_msgpack_rejects_reference() +{ + char *buffer; + size_t size; + struct ctrace *ctx; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + struct ctrace_span *span; + + ctx = ctr_create(NULL); + rs = ctr_resource_span_create(ctx); + ss = ctr_scope_span_create(rs); + span = ctr_span_create(ctx, ss, "reference", NULL); + TEST_ASSERT(cfl_kvlist_insert_reference(span->attr->kv, "opaque", span) == 0); + + buffer = (char *) 0x1; + size = 123; + TEST_CHECK(ctr_encode_msgpack_create(ctx, &buffer, &size) != 0); + TEST_CHECK(buffer == NULL); + TEST_CHECK(size == 0); + + ctr_destroy(ctx); +} + +void test_msgpack_rejects_duplicate_fields() +{ + char *buffer; + size_t size; + size_t offset; + mpack_writer_t writer; + struct ctrace *decoded; + int result; + + mpack_writer_init_growable(&writer, &buffer, &size); + mpack_start_map(&writer, 2); + mpack_write_cstr(&writer, "resourceSpans"); + mpack_start_array(&writer, 0); + mpack_finish_array(&writer); + mpack_write_cstr(&writer, "resourceSpans"); + mpack_start_array(&writer, 0); + mpack_finish_array(&writer); + mpack_finish_map(&writer); + TEST_ASSERT(mpack_writer_destroy(&writer) == mpack_ok); + + offset = 0; + decoded = NULL; + result = ctr_decode_msgpack_create(&decoded, buffer, size, &offset); + TEST_CHECK(result == CTR_MPACK_CORRUPT_INPUT_DATA_ERROR); + TEST_CHECK(decoded == NULL); + + free(buffer); +} + void test_simple_to_msgpack_and_back() { struct ctrace *ctx; @@ -734,5 +973,12 @@ TEST_LIST = { {"cmt_simple_to_msgpack_and_back", test_simple_to_msgpack_and_back}, {"cmt_msgpack", test_msgpack_to_cmt}, {"empty_spans", test_msgpack_to_ctr_with_empty_spans}, + {"msgpack_preserves_flags", test_msgpack_preserves_flags}, + {"msgpack_invalid_offset", test_msgpack_invalid_offset}, + {"msgpack_integer_ranges", test_msgpack_integer_ranges}, + {"msgpack_long_attribute_key", test_msgpack_long_attribute_key}, + {"msgpack_variant_limits", test_msgpack_variant_limits}, + {"msgpack_rejects_reference", test_msgpack_rejects_reference}, + {"msgpack_rejects_duplicate_fields", test_msgpack_rejects_duplicate_fields}, { 0 } }; diff --git a/tests/opentelemetry.c b/tests/opentelemetry.c index 4525cc8..80daa73 100644 --- a/tests/opentelemetry.c +++ b/tests/opentelemetry.c @@ -68,6 +68,8 @@ static struct ctrace *build_sample_trace() ctr_span_set_trace_id_with_cid(span_root, trace_id); ctr_span_set_span_id_with_cid(span_root, span_id); ctr_span_set_status(span_root, CTRACE_SPAN_STATUS_CODE_OK, "all good"); + ctr_span_set_flags(span_root, 0x301); + ctr_span_set_dropped_links_count(span_root, 11); ctr_span_set_attribute_string(span_root, "service.name", "ctraces"); ctr_span_set_attribute_int64(span_root, "year", 2026); @@ -95,6 +97,7 @@ static struct ctrace *build_sample_trace() link = ctr_link_create_with_cid(span_child, trace_id, span_id); ctr_link_set_trace_state(link, "state=1"); ctr_link_set_dropped_attr_count(link, 2); + ctr_link_set_flags(link, 0x201); ctr_id_destroy(trace_id); ctr_id_destroy(span_id); @@ -131,6 +134,10 @@ void test_otlp_roundtrip() struct ctrace *ctx; struct ctrace *decoded; int ret; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + struct ctrace_span *span; + struct ctrace_link *link; ctx = build_sample_trace(); TEST_ASSERT(ctx != NULL); @@ -144,6 +151,18 @@ void test_otlp_roundtrip() TEST_ASSERT(decoded != NULL); TEST_CHECK(count_spans(decoded) == 2); + rs = cfl_list_entry(decoded->resource_spans.next, + struct ctrace_resource_span, _head); + ss = cfl_list_entry(rs->scope_spans.next, struct ctrace_scope_span, _head); + span = cfl_list_entry(ss->spans.next, struct ctrace_span, _head); + TEST_CHECK(span->flags == 0x301); + TEST_CHECK(span->dropped_links_count == 11); + + span = cfl_list_entry(span->_head.next, struct ctrace_span, _head); + link = cfl_list_entry(span->links.next, struct ctrace_link, _head); + TEST_CHECK(link->flags == 0x201); + TEST_CHECK(strcmp(link->trace_state, "state=1") == 0); + ctr_encode_opentelemetry_destroy(buf); ctr_destroy(decoded); ctr_destroy(ctx); @@ -203,6 +222,94 @@ void test_otlp_minimal_trace() ctr_destroy(ctx); } +void test_otlp_empty_context() +{ + cfl_sds_t buf; + size_t offset; + struct ctrace *ctx; + struct ctrace *decoded; + int ret; + + ctx = ctr_create(NULL); + buf = ctr_encode_opentelemetry_create(ctx); + TEST_ASSERT(buf != NULL); + + offset = 0; + ret = ctr_decode_opentelemetry_create(&decoded, buf, cfl_sds_len(buf), &offset); + TEST_ASSERT(ret == CTR_DECODE_OPENTELEMETRY_SUCCESS); + TEST_CHECK(cfl_list_is_empty(&decoded->resource_spans)); + + ctr_destroy(decoded); + ctr_encode_opentelemetry_destroy(buf); + ctr_destroy(ctx); +} + +void test_otlp_empty_bytes_attribute() +{ + cfl_sds_t buf; + struct ctrace *ctx; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + struct ctrace_span *span; + + ctx = ctr_create(NULL); + rs = ctr_resource_span_create(ctx); + ss = ctr_scope_span_create(rs); + span = ctr_span_create(ctx, ss, "empty-bytes", NULL); + TEST_ASSERT(cfl_kvlist_insert_bytes(span->attr->kv, "empty", "", 0, + CFL_FALSE) == 0); + + buf = ctr_encode_opentelemetry_create(ctx); + TEST_CHECK(buf != NULL); + + ctr_encode_opentelemetry_destroy(buf); + ctr_destroy(ctx); +} + +void test_otlp_rejects_invalid_variant() +{ + cfl_sds_t buf; + struct ctrace *ctx; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + struct ctrace_span *span; + struct cfl_variant *value; + + ctx = ctr_create(NULL); + rs = ctr_resource_span_create(ctx); + ss = ctr_scope_span_create(rs); + span = ctr_span_create(ctx, ss, "invalid", NULL); + value = cfl_variant_create(); + TEST_ASSERT(value != NULL); + value->type = 999; + TEST_ASSERT(cfl_kvlist_insert(span->attr->kv, "invalid", value) == 0); + + buf = ctr_encode_opentelemetry_create(ctx); + TEST_CHECK(buf == NULL); + + ctr_destroy(ctx); +} + +void test_otlp_rejects_opaque_reference() +{ + cfl_sds_t buf; + struct ctrace *ctx; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + struct ctrace_span *span; + + ctx = ctr_create(NULL); + rs = ctr_resource_span_create(ctx); + ss = ctr_scope_span_create(rs); + span = ctr_span_create(ctx, ss, "reference", NULL); + TEST_ASSERT(cfl_kvlist_insert_reference(span->attr->kv, "opaque", span) == 0); + + buf = ctr_encode_opentelemetry_create(ctx); + TEST_CHECK(buf == NULL); + + ctr_destroy(ctx); +} + /* attributes carry bytes only when wrapped in an array or kvlist * (convert_bytes_value rejects raw bytes at attribute top-level). * This exercises ctr_variant_binary_to_otlp_any_value end-to-end. @@ -241,6 +348,46 @@ void test_otlp_bytes_in_array() ctr_destroy(ctx); } +void test_otlp_bytes_attribute() +{ + cfl_sds_t buf; + size_t offset = 0; + struct ctrace *ctx; + struct ctrace *decoded = NULL; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + struct ctrace_span *span; + struct cfl_variant *value; + int ret; + + ctx = ctr_create(NULL); + rs = ctr_resource_span_create(ctx); + ss = ctr_scope_span_create(rs); + span = ctr_span_create(ctx, ss, "bytes", NULL); + TEST_ASSERT(span != NULL); + TEST_ASSERT(cfl_kvlist_insert_bytes(span->attr->kv, "blob", "\xDE\xAD", 2, + CFL_FALSE) == 0); + + buf = ctr_encode_opentelemetry_create(ctx); + TEST_ASSERT(buf != NULL); + ret = ctr_decode_opentelemetry_create(&decoded, buf, cfl_sds_len(buf), &offset); + TEST_ASSERT(ret == CTR_DECODE_OPENTELEMETRY_SUCCESS); + + rs = cfl_list_entry(decoded->resource_spans.next, + struct ctrace_resource_span, _head); + ss = cfl_list_entry(rs->scope_spans.next, struct ctrace_scope_span, _head); + span = cfl_list_entry(ss->spans.next, struct ctrace_span, _head); + value = cfl_kvlist_fetch(span->attr->kv, "blob"); + TEST_ASSERT(value != NULL); + TEST_CHECK(value->type == CFL_VARIANT_BYTES); + TEST_CHECK(cfl_sds_len(value->data.as_bytes) == 2); + TEST_CHECK(memcmp(value->data.as_bytes, "\xDE\xAD", 2) == 0); + + ctr_encode_opentelemetry_destroy(buf); + ctr_destroy(decoded); + ctr_destroy(ctx); +} + /* multiple resource_spans, each with multiple scope_spans, each with * multiple spans - exercises every encoder/decoder outer loop including * the per-resource_span/per-scope_span/per-span destroy paths. @@ -523,10 +670,39 @@ void test_otlp_decode_span_empty_name() free(wire); } +void test_otlp_decode_invalid_kind() +{ + Opentelemetry__Proto__Trace__V1__Span span; + uint8_t *wire; + size_t wire_len; + size_t offset; + struct ctrace *decoded; + int ret; + + opentelemetry__proto__trace__v1__span__init(&span); + span.name = "invalid-kind"; + span.kind = 99; + wire = pack_one_span_payload(&span, &wire_len); + TEST_ASSERT(wire != NULL); + + offset = 0; + decoded = NULL; + ret = ctr_decode_opentelemetry_create(&decoded, (char *) wire, wire_len, &offset); + TEST_CHECK(ret != CTR_DECODE_OPENTELEMETRY_SUCCESS); + TEST_CHECK(decoded == NULL); + + free(wire); +} + TEST_LIST = { {"otlp_roundtrip", test_otlp_roundtrip}, {"otlp_minimal_trace", test_otlp_minimal_trace}, + {"otlp_empty_context", test_otlp_empty_context}, + {"otlp_empty_bytes_attribute", test_otlp_empty_bytes_attribute}, + {"otlp_rejects_invalid_variant", test_otlp_rejects_invalid_variant}, + {"otlp_rejects_opaque_reference", test_otlp_rejects_opaque_reference}, {"otlp_bytes_in_array", test_otlp_bytes_in_array}, + {"otlp_bytes_attribute", test_otlp_bytes_attribute}, {"otlp_multiple_spans", test_otlp_multiple_spans}, {"otlp_decode_corrupted", test_otlp_decode_corrupted}, {"otlp_decode_missing_resource", test_otlp_decode_missing_resource}, @@ -534,5 +710,6 @@ TEST_LIST = { {"otlp_decode_attribute_null_value", test_otlp_decode_attribute_null_value}, {"otlp_decode_nested_malformed_kv", test_otlp_decode_nested_malformed_kv}, {"otlp_decode_span_empty_name", test_otlp_decode_span_empty_name}, + {"otlp_decode_invalid_kind", test_otlp_decode_invalid_kind}, { 0 } }; diff --git a/tests/span.c b/tests/span.c index 42427d2..420af7f 100644 --- a/tests/span.c +++ b/tests/span.c @@ -120,9 +120,138 @@ void test_random_id_length() ctr_id_destroy(id); } +void test_event_integer_api() +{ + struct ctrace *ctx; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + struct ctrace_span *span; + struct ctrace_span_event *event; + struct cfl_variant *value; + + ctx = ctr_create(NULL); + rs = ctr_resource_span_create(ctx); + ss = ctr_scope_span_create(rs); + span = ctr_span_create(ctx, ss, "event", NULL); + event = ctr_span_event_add(span, "integer"); + TEST_ASSERT(event != NULL); + + TEST_CHECK(ctr_span_event_set_attribute_int(event, "int", 42) == 0); + TEST_CHECK(ctr_span_event_set_attribute_int64(event, "int64", INT64_MAX) == 0); + value = cfl_kvlist_fetch(event->attr->kv, "int64"); + TEST_ASSERT(value != NULL); + TEST_CHECK(value->data.as_int64 == INT64_MAX); + + ctr_destroy(ctx); +} + +void test_text_encoder_optional_and_long_strings() +{ + char long_value[2048]; + cfl_sds_t text; + struct ctrace *ctx; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + struct ctrace_instrumentation_scope *scope; + struct ctrace_span *span; + struct ctrace_link *link; + + memset(long_value, 'x', sizeof(long_value) - 1); + long_value[sizeof(long_value) - 1] = '\0'; + + ctx = ctr_create(NULL); + rs = ctr_resource_span_create(ctx); + ss = ctr_scope_span_create(rs); + scope = ctr_instrumentation_scope_create(NULL, NULL, 0, NULL); + ctr_scope_span_set_instrumentation_scope(ss, scope); + span = ctr_span_create(ctx, ss, "text", NULL); + link = ctr_link_create(span, NULL, 0, NULL, 0); + TEST_ASSERT(link != NULL); + TEST_CHECK(ctr_span_set_attribute_string(span, "long", long_value) == 0); + + text = ctr_encode_text_create(ctx); + TEST_ASSERT(text != NULL); + TEST_CHECK(strstr(text, long_value) != NULL); + + ctr_encode_text_destroy(text); + ctr_destroy(ctx); +} + +void test_owner_self_assignment() +{ + struct ctrace *ctx; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + struct ctrace_instrumentation_scope *scope; + struct ctrace_span *span; + struct ctrace_span_event *event; + + ctx = ctr_create(NULL); + rs = ctr_resource_span_create(ctx); + ss = ctr_scope_span_create(rs); + scope = ctr_instrumentation_scope_create("scope", "1", 0, NULL); + ctr_scope_span_set_instrumentation_scope(ss, scope); + span = ctr_span_create(ctx, ss, "self", NULL); + event = ctr_span_event_add(span, "self"); + + TEST_CHECK(ctr_span_set_attributes(span, span->attr) == 0); + TEST_CHECK(ctr_span_event_set_attributes(event, event->attr) == 0); + ctr_scope_span_set_instrumentation_scope(ss, ss->instrumentation_scope); + TEST_CHECK(strcmp(ss->instrumentation_scope->name, "scope") == 0); + + ctr_destroy(ctx); +} + +void test_reject_cross_context_span() +{ + struct ctrace *ctx_a; + struct ctrace *ctx_b; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + + ctx_a = ctr_create(NULL); + ctx_b = ctr_create(NULL); + rs = ctr_resource_span_create(ctx_a); + ss = ctr_scope_span_create(rs); + + TEST_CHECK(ctr_span_create(ctx_b, ss, "invalid", NULL) == NULL); + TEST_CHECK(cfl_list_is_empty(&ctx_b->span_list)); + TEST_CHECK(cfl_list_is_empty(&ss->spans)); + + ctr_destroy(ctx_b); + ctr_destroy(ctx_a); +} + +void test_reject_invalid_span_enums() +{ + struct ctrace *ctx; + struct ctrace_resource_span *rs; + struct ctrace_scope_span *ss; + struct ctrace_span *span; + + ctx = ctr_create(NULL); + rs = ctr_resource_span_create(ctx); + ss = ctr_scope_span_create(rs); + span = ctr_span_create(ctx, ss, "enums", NULL); + + TEST_CHECK(ctr_span_kind_set(span, CTRACE_SPAN_CONSUMER + 1) != 0); + TEST_CHECK(span->kind == CTRACE_SPAN_INTERNAL); + TEST_CHECK(ctr_span_set_status(span, CTRACE_SPAN_STATUS_CODE_ERROR + 1, + "invalid") != 0); + TEST_CHECK(span->status.code == CTRACE_SPAN_STATUS_CODE_UNSET); + TEST_CHECK(span->status.message == NULL); + + ctr_destroy(ctx); +} + TEST_LIST = { {"span", test_span}, {"resource_span_direct_destroy", test_resource_span_direct_destroy}, {"random_id_length", test_random_id_length}, + {"event_integer_api", test_event_integer_api}, + {"text_encoder_optional_and_long_strings", test_text_encoder_optional_and_long_strings}, + {"owner_self_assignment", test_owner_self_assignment}, + {"reject_cross_context_span", test_reject_cross_context_span}, + {"reject_invalid_span_enums", test_reject_invalid_span_enums}, { 0 } };