From 1169297cfb28829ff368038855e02d745d24e741 Mon Sep 17 00:00:00 2001 From: Ben Haller Date: Thu, 12 Mar 2026 16:04:10 -0600 Subject: [PATCH] add alignment bytes to the `json_struct` codec --- treerec/tskit/core.c | 39 +++++++++++++++++++++++++++++++++------ treerec/tskit/core.h | 19 +++++++++++++++---- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/treerec/tskit/core.c b/treerec/tskit/core.c index 1a56bc47..5e2b21be 100644 --- a/treerec/tskit/core.c +++ b/treerec/tskit/core.c @@ -151,10 +151,13 @@ tsk_json_struct_metadata_get_blob(char *metadata, tsk_size_t metadata_length, uint64_t json_length_u64; uint64_t binary_length_u64; uint64_t header_and_json_length; + uint64_t padding_length; + uint64_t header_and_json_and_padding_length; uint64_t total_length; uint8_t *bytes; - char *blob_start; char *json_start; + char *padding_start; + char *blob_start; if (metadata == NULL || json == NULL || json_length == NULL || blob == NULL || blob_length == NULL) { @@ -182,17 +185,33 @@ tsk_json_struct_metadata_get_blob(char *metadata, tsk_size_t metadata_length, goto out; } header_and_json_length = (uint64_t) TSK_JSON_BINARY_HEADER_SIZE + json_length_u64; - if (binary_length_u64 > UINT64_MAX - header_and_json_length) { + padding_length = (8 - (header_and_json_length & 0x07) % 8); + if (padding_length > UINT64_MAX - header_and_json_length) { ret = tsk_trace_error(TSK_ERR_JSON_STRUCT_METADATA_INVALID_LENGTH); goto out; } - total_length = header_and_json_length + binary_length_u64; - if ((uint64_t) metadata_length < total_length) { - ret = tsk_trace_error(TSK_ERR_JSON_STRUCT_METADATA_TRUNCATED); + header_and_json_and_padding_length = header_and_json_length + padding_length; + if (binary_length_u64 > UINT64_MAX - header_and_json_and_padding_length) { + ret = tsk_trace_error(TSK_ERR_JSON_STRUCT_METADATA_INVALID_LENGTH); + goto out; + } + total_length = header_and_json_and_padding_length + binary_length_u64; + if ((uint64_t) metadata_length != total_length) { + ret = tsk_trace_error(TSK_ERR_JSON_STRUCT_METADATA_UNEXPECTED_SIZE); goto out; } + padding_start = (char *) bytes + TSK_JSON_BINARY_HEADER_SIZE + json_length_u64; + for (uint64_t padding_index = 0; padding_index < padding_length; ++padding_index) + { + // require padding bytes to be zero, for a bit more safety + if (*(padding_start + padding_index) != (char)0) + { + ret = tsk_trace_error(TSK_ERR_JSON_STRUCT_METADATA_NONZERO_PADDING); + goto out; + } + } json_start = (char *) bytes + TSK_JSON_BINARY_HEADER_SIZE; - blob_start = (char *) bytes + TSK_JSON_BINARY_HEADER_SIZE + json_length_u64; + blob_start = (char *) bytes + TSK_JSON_BINARY_HEADER_SIZE + json_length_u64 + padding_length; *json = json_start; *json_length = (tsk_size_t) json_length_u64; *blob = blob_start; @@ -284,6 +303,14 @@ tsk_strerror_internal(int err) ret = "JSON binary struct metadata uses an unsupported version number. " "(TSK_ERR_JSON_STRUCT_METADATA_BAD_VERSION)"; break; + case TSK_ERR_JSON_STRUCT_METADATA_UNEXPECTED_SIZE: + ret = "JSON binary struct metadata is not equal to the expected size. " + "(TSK_ERR_JSON_STRUCT_METADATA_UNEXPECTED_SIZE)"; + break; + case TSK_ERR_JSON_STRUCT_METADATA_NONZERO_PADDING: + ret = "JSON binary struct metadata has non-zero padding bytes between the " + "JSON and struct. (TSK_ERR_JSON_STRUCT_METADATA_NONZERO_PADDING)"; + break; /* Out of bounds errors */ case TSK_ERR_BAD_OFFSET: diff --git a/treerec/tskit/core.h b/treerec/tskit/core.h index 0ebb2344..2bb3e03e 100644 --- a/treerec/tskit/core.h +++ b/treerec/tskit/core.h @@ -329,6 +329,16 @@ A length field in the JSON binary struct metadata header is invalid. The JSON binary struct metadata uses an unsupported version number. */ #define TSK_ERR_JSON_STRUCT_METADATA_BAD_VERSION -109 + +/** +The JSON binary struct metadata is not equal to the expected size. +*/ +#define TSK_ERR_JSON_STRUCT_METADATA_UNEXPECTED_SIZE -110 + +/** +The JSON binary struct metadata is not equal to the expected size. +*/ +#define TSK_ERR_JSON_STRUCT_METADATA_NONZERO_PADDING -111 /** @} */ /** @@ -1136,10 +1146,11 @@ int tsk_generate_uuid(char *dest, int flags); @brief Extract the binary payload from ``json+struct`` encoded metadata. @rst -Metadata produced by the JSONStructCodec consists of a fixed-size -header followed by canonical JSON bytes and an optional binary payload. This helper -validates the framing, returning pointers to the embedded JSON and binary sections -without copying. +Metadata produced by the JSONStructCodec consists of a fixed-size header followed +by canonical JSON bytes, a variable number of padding bytes (which should be zero) +to bring the length to a multiple of 8 bytes for alignment, and finally an optional +binary payload. This helper validates the framing, returning pointers to the +embedded JSON and binary sections without copying. The output pointers reference memory owned by the caller and remain valid only while the original metadata buffer is alive.