diff --git a/src/ua_types_encoding_binary.c b/src/ua_types_encoding_binary.c index 3f03c2966..07a8cda4b 100644 --- a/src/ua_types_encoding_binary.c +++ b/src/ua_types_encoding_binary.c @@ -894,13 +894,21 @@ ExtensionObject_decodeBinaryContent(UA_ExtensionObject *dst, const UA_NodeId *ty dst->content.decoded.data = UA_new(type); UA_CHECK_MEM(dst->content.decoded.data, return UA_STATUSCODE_BADOUTOFMEMORY); - /* Jump over the length field (TODO: check if the decoded length matches) */ - ctx->pos += 4; + /* Read the length field and validate that the inner decoder consumes exactly + * that many bytes, closing a decoder-vs-IDS split-view channel. */ + u32 member_length = 0; + status ret = DECODE_DIRECT(&member_length, UInt32); + UA_CHECK_STATUS(ret, return ret); + UA_CHECK(ctx->pos + member_length <= ctx->end, return UA_STATUSCODE_BADDECODINGERROR); + const u8 *expected_end = ctx->pos + member_length; /* Decode */ dst->encoding = UA_EXTENSIONOBJECT_DECODED; dst->content.decoded.type = type; - return decodeBinaryJumpTable[type->typeKind](dst->content.decoded.data, type, ctx); + ret = decodeBinaryJumpTable[type->typeKind](dst->content.decoded.data, type, ctx); + if(ret == UA_STATUSCODE_GOOD && ctx->pos != expected_end) + return UA_STATUSCODE_BADDECODINGERROR; + return ret; } DECODE_BINARY(ExtensionObject) { @@ -1057,10 +1065,17 @@ Variant_decodeBinaryUnwrapExtensionObject(UA_Variant *dst, Ctx *ctx) { UA_CHECK_STATUS(ret, UA_NodeId_clear(&typeId); return ret); /* Search for the datatype. Default to ExtensionObject. */ + const u8 *expected_end = NULL; if(encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING && (dst->type = UA_findDataTypeByBinaryInternal(&typeId, ctx)) != NULL) { - /* Jump over the length field (TODO: check if length matches) */ - ctx->pos += 4; + /* Read the length field and validate that the inner decoder consumes + * exactly that many bytes, closing a decoder-vs-IDS split-view channel. */ + u32 member_length = 0; + ret = DECODE_DIRECT(&member_length, UInt32); + UA_CHECK_STATUS(ret, UA_NodeId_clear(&typeId); return ret); + UA_CHECK(ctx->pos + member_length <= ctx->end, + UA_NodeId_clear(&typeId); return UA_STATUSCODE_BADDECODINGERROR); + expected_end = ctx->pos + member_length; } else { /* Reset and decode as ExtensionObject */ dst->type = &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]; @@ -1073,7 +1088,10 @@ Variant_decodeBinaryUnwrapExtensionObject(UA_Variant *dst, Ctx *ctx) { UA_CHECK_MEM(dst->data, return UA_STATUSCODE_BADOUTOFMEMORY); /* Decode the content */ - return decodeBinaryJumpTable[dst->type->typeKind](dst->data, dst->type, ctx); + ret = decodeBinaryJumpTable[dst->type->typeKind](dst->data, dst->type, ctx); + if(ret == UA_STATUSCODE_GOOD && expected_end != NULL && ctx->pos != expected_end) + return UA_STATUSCODE_BADDECODINGERROR; + return ret; } /* The resulting variant always has the storagetype UA_VARIANT_DATA. */