From cb343b6de673184e4e9327d170017f542133decd Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 30 Jul 2025 07:43:51 +0100 Subject: [PATCH 1/2] - fixed BER decoder infinite length handling to avoid inifite loop while decoding (LIB61850-516) --- src/mms/iso_mms/asn1c/constr_CHOICE.c | 52 +++++++----- src/mms/iso_mms/asn1c/constr_SEQUENCE.c | 107 ++++++++++++++---------- src/mms/iso_mms/asn1c/constr_SET_OF.c | 37 ++++---- 3 files changed, 116 insertions(+), 80 deletions(-) diff --git a/src/mms/iso_mms/asn1c/constr_CHOICE.c b/src/mms/iso_mms/asn1c/constr_CHOICE.c index c1db22f3..dd5d241f 100644 --- a/src/mms/iso_mms/asn1c/constr_CHOICE.c +++ b/src/mms/iso_mms/asn1c/constr_CHOICE.c @@ -291,7 +291,8 @@ CHOICE_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, /* * Read in the "end of data chunks"'s. */ - while(ctx->left < 0) { + while(ctx->left < 0) + { ssize_t tl; tl = ber_fetch_tag(ptr, LEFT, &tlv_tag); @@ -304,25 +305,36 @@ CHOICE_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, /* * Expected <0><0>... */ - if(((const uint8_t *)ptr)[0] == 0) { - if(LEFT < 2) { - if(SIZE_VIOLATION) - RETURN(RC_FAIL); - else - RETURN(RC_WMORE); - } else if(((const uint8_t *)ptr)[1] == 0) { - /* - * Correctly finished with <0><0>. - */ - ADVANCE(2); - ctx->left++; - continue; - } - } else { - RETURN(RC_FAIL); - } - - /* UNREACHABLE */ + if (((const uint8_t*)ptr)[0] == 0) + { + if (LEFT < 2) + { + if (SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } + else if (((const uint8_t*)ptr)[1] == 0) + { + /* + * Correctly finished with <0><0>. + */ + ADVANCE(2); + ctx->left++; + continue; + } + else + { + /* this case should not happen when the message is correctly encoded */ + RETURN(RC_FAIL); + } + } + else + { + RETURN(RC_FAIL); + } + + /* UNREACHABLE */ } NEXT_PHASE(ctx); diff --git a/src/mms/iso_mms/asn1c/constr_SEQUENCE.c b/src/mms/iso_mms/asn1c/constr_SEQUENCE.c index 35aec840..a665e8ac 100644 --- a/src/mms/iso_mms/asn1c/constr_SEQUENCE.c +++ b/src/mms/iso_mms/asn1c/constr_SEQUENCE.c @@ -223,34 +223,41 @@ SEQUENCE_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, case -1: RETURN(RC_FAIL); } - if(ctx->left < 0 && ((const uint8_t *)ptr)[0] == 0) { - if(LEFT < 2) { - if(SIZE_VIOLATION) - RETURN(RC_FAIL); - else - RETURN(RC_WMORE); - } else if(((const uint8_t *)ptr)[1] == 0) { - - if((edx + elements[edx].optional - == td->elements_count) - || (IN_EXTENSION_GROUP(specs, edx) - && specs->ext_before - > td->elements_count)) { - /* - * Yeah, baby! Found the terminator - * of the indefinite length structure. - */ - /* - * Proceed to the canonical - * finalization function. - * No advancing is necessary. - */ - goto phase3; - } - } - } - - /* + if (ctx->left < 0 && ((const uint8_t*)ptr)[0] == 0) + { + if (LEFT < 2) + { + if (SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } + else if (((const uint8_t*)ptr)[1] == 0) + { + + if ((edx + elements[edx].optional == td->elements_count) || + (IN_EXTENSION_GROUP(specs, edx) && specs->ext_before > td->elements_count)) + { + /* + * Yeah, baby! Found the terminator + * of the indefinite length structure. + */ + /* + * Proceed to the canonical + * finalization function. + * No advancing is necessary. + */ + goto phase3; + } + } + else + { + /* this case should not happen when the message is correctly encoded */ + RETURN(RC_FAIL); + } + } + + /* * Find the next available type with this tag. */ use_bsearch = 0; @@ -421,25 +428,33 @@ SEQUENCE_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, /* * If expected <0><0>... */ - if(ctx->left < 0 - && ((const uint8_t *)ptr)[0] == 0) { - if(LEFT < 2) { - if(SIZE_VIOLATION) - RETURN(RC_FAIL); - else - RETURN(RC_WMORE); - } else if(((const uint8_t *)ptr)[1] == 0) { - /* - * Correctly finished with <0><0>. - */ - ADVANCE(2); - ctx->left++; - ctx->phase = 4; - continue; - } - } + if (ctx->left < 0 && ((const uint8_t*)ptr)[0] == 0) + { + if (LEFT < 2) + { + if (SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } + else if (((const uint8_t*)ptr)[1] == 0) + { + /* + * Correctly finished with <0><0>. + */ + ADVANCE(2); + ctx->left++; + ctx->phase = 4; + continue; + } + else + { + /* this case should not happen when the message is correctly encoded */ + RETURN(RC_FAIL); + } + } - if(!IN_EXTENSION_GROUP(specs, td->elements_count) + if(!IN_EXTENSION_GROUP(specs, td->elements_count) || ctx->phase == 4) { RETURN(RC_FAIL); } diff --git a/src/mms/iso_mms/asn1c/constr_SET_OF.c b/src/mms/iso_mms/asn1c/constr_SET_OF.c index 85f78c97..8e35ac2e 100644 --- a/src/mms/iso_mms/asn1c/constr_SET_OF.c +++ b/src/mms/iso_mms/asn1c/constr_SET_OF.c @@ -158,22 +158,31 @@ SET_OF_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, case -1: RETURN(RC_FAIL); } - if(ctx->left < 0 && ((const uint8_t *)ptr)[0] == 0) { - if(LEFT < 2) { - if(SIZE_VIOLATION) - RETURN(RC_FAIL); - else - RETURN(RC_WMORE); - } else if(((const uint8_t *)ptr)[1] == 0) { - /* - * Found the terminator of the - * indefinite length structure. - */ - break; + if (ctx->left < 0 && ((const uint8_t*)ptr)[0] == 0) + { + if (LEFT < 2) + { + if (SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } + else if (((const uint8_t*)ptr)[1] == 0) + { + /* + * Found the terminator of the + * indefinite length structure. + */ + break; + } + else + { + /* this case should not happen when the message is correctly encoded */ + RETURN(RC_FAIL); } - } + } - /* Outmost tag may be unknown and cannot be fetched/compared */ + /* Outmost tag may be unknown and cannot be fetched/compared */ if(elm->tag != (ber_tlv_tag_t)-1) { if(BER_TAGS_EQUAL(tlv_tag, elm->tag)) { /* From 7d6b3cce69574cb9a0cd7af6d1d4c777746ffc90 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 30 Jul 2025 09:26:54 +0100 Subject: [PATCH 2/2] - code format update --- src/mms/iso_presentation/iso_presentation.c | 167 +++++++++++++------- 1 file changed, 108 insertions(+), 59 deletions(-) diff --git a/src/mms/iso_presentation/iso_presentation.c b/src/mms/iso_presentation/iso_presentation.c index c4f2f64e..08168609 100644 --- a/src/mms/iso_presentation/iso_presentation.c +++ b/src/mms/iso_presentation/iso_presentation.c @@ -1,7 +1,7 @@ /* * iso_presentation.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2025 Michael Zillgith * * This file is part of libIEC61850. * @@ -35,7 +35,6 @@ #define DEBUG_PRES 0 #endif - static uint8_t calledPresentationSelector[] = { 0x00, 0x00, 0x00, 0x01 }; static uint8_t asn_id_as_acse[] = { 0x52, 0x01, 0x00, 0x01 }; @@ -73,7 +72,8 @@ encodeUserData(uint8_t* buffer, int bufPos, fullyEncodedDataLength += BerEncoder_determineLengthSize(encodedDataSetLength) + 1; - if (encode) { + if (encode) + { /* fully-encoded-data */ bufPos = BerEncoder_encodeTL(0x61, fullyEncodedDataLength, buffer, bufPos); bufPos = BerEncoder_encodeTL(0x30, encodedDataSetLength, buffer, bufPos); @@ -87,7 +87,8 @@ encodeUserData(uint8_t* buffer, int bufPos, return bufPos; } - else { + else + { int encodedUserDataLength = fullyEncodedDataLength + 1; encodedUserDataLength += BerEncoder_determineLengthSize(fullyEncodedDataLength); @@ -195,7 +196,8 @@ parseFullyEncodedData(IsoPresentation* self, uint8_t* buffer, int len, int bufPo int endPos = bufPos + len; - if (buffer[bufPos++] != 0x30) { + if (buffer[bufPos++] != 0x30) + { if (DEBUG_PRES) printf("PRES: user-data parse error\n"); return -1; @@ -203,7 +205,8 @@ parseFullyEncodedData(IsoPresentation* self, uint8_t* buffer, int len, int bufPo bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, endPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: wrong parameter length\n"); return -1; @@ -211,19 +214,22 @@ parseFullyEncodedData(IsoPresentation* self, uint8_t* buffer, int len, int bufPo endPos = bufPos + len; - while (bufPos < endPos) { + while (bufPos < endPos) + { uint8_t tag = buffer[bufPos++]; int length; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, endPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: wrong parameter length\n"); return -1; } - switch (tag) { + switch (tag) + { case 0x02: /* presentation-context-identifier */ if (DEBUG_PRES) printf("PRES: presentation-context-identifier\n"); @@ -262,7 +268,8 @@ parseFullyEncodedData(IsoPresentation* self, uint8_t* buffer, int len, int bufPo } } - if (!userDataPresent) { + if (!userDataPresent) + { if (DEBUG_PRES) printf("PRES: user-data not present\n"); return -1; @@ -280,13 +287,15 @@ parsePCDLEntry(IsoPresentation* self, uint8_t* buffer, int totalLength, int bufP bool isAcse = false; bool isMms = false; - while (bufPos < endPos) { + while (bufPos < endPos) + { uint8_t tag = buffer[bufPos++]; int len; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, endPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: Invalid PDU\n"); return -1; @@ -302,11 +311,13 @@ parsePCDLEntry(IsoPresentation* self, uint8_t* buffer, int totalLength, int bufP if (DEBUG_PRES) printf("PRES: abstract-syntax-name with len %i\n", len); - if (len == 5) { + if (len == 5) + { if (memcmp(buffer + bufPos, asn_id_mms, 5) == 0) isMms = true; } - else if (len == 4) { + else if (len == 4) + { if (memcmp(buffer + bufPos, asn_id_as_acse, 4) == 0) isAcse = true; } @@ -333,26 +344,30 @@ parsePCDLEntry(IsoPresentation* self, uint8_t* buffer, int totalLength, int bufP } } - if (contextId < 0) { + if (contextId < 0) + { if (DEBUG_PRES) printf("PRES: ContextId not defined!\n"); return -1; } - if ((isAcse == false) && (isMms == false)) { + if ((isAcse == false) && (isMms == false)) + { if (DEBUG_PRES) printf("PRES: not an ACSE or MMS context definition\n"); return -1; } - if (isMms) { - self->mmsContextId = (uint8_t) contextId; + if (isMms) + { + self->mmsContextId = (uint8_t)contextId; if (DEBUG_PRES) printf("PRES: MMS context id is %i\n", contextId); } - else { - self->acseContextId = (uint8_t) contextId; + else + { + self->acseContextId = (uint8_t)contextId; if (DEBUG_PRES) printf("PRES: ACSE context id is %i\n", contextId); } @@ -365,7 +380,8 @@ parsePresentationContextDefinitionList(IsoPresentation* self, uint8_t* buffer, i { int endPos = bufPos + totalLength; - while (bufPos < endPos) { + while (bufPos < endPos) + { uint8_t tag = buffer[bufPos++]; int len; @@ -373,7 +389,8 @@ parsePresentationContextDefinitionList(IsoPresentation* self, uint8_t* buffer, i if (bufPos < 0) return -1; - switch (tag) { + switch (tag) + { case 0x30: if (DEBUG_PRES) printf("PRES: parse pcd entry\n"); @@ -404,11 +421,13 @@ parseNormalModeParameters(IsoPresentation* self, uint8_t* buffer, int totalLengt bool hasUserData = false; - while (bufPos < endPos) { + while (bufPos < endPos) + { uint8_t tag = buffer[bufPos++]; int len; - if (bufPos == endPos) { + if (bufPos == endPos) + { if (DEBUG_PRES) printf("PRES: invalid message\n"); return -1; @@ -416,20 +435,24 @@ parseNormalModeParameters(IsoPresentation* self, uint8_t* buffer, int totalLengt bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, endPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: wrong parameter length\n"); return -1; } - switch (tag) { + switch (tag) + { case 0x81: /* calling-presentation-selector */ - if (len > 16) { + if (len > 16) + { if (DEBUG_PRES) printf("PRES: calling-presentation-sel too large\n"); } - else { + else + { self->callingPresentationSelector.size = len; int i; for (i = 0; i < len; i++) @@ -441,11 +464,13 @@ parseNormalModeParameters(IsoPresentation* self, uint8_t* buffer, int totalLengt case 0x82: /* called-presentation-selector */ - if (len > 16) { + if (len > 16) + { if (DEBUG_PRES) printf("PRES: called-presentation-sel too large\n"); } - else { + else + { self->calledPresentationSelector.size = len; int i; for (i = 0; i < len; i++) @@ -457,7 +482,8 @@ parseNormalModeParameters(IsoPresentation* self, uint8_t* buffer, int totalLengt case 0x83: /* responding-presentation-selector */ - if (len > 16) { + if (len > 16) + { if (DEBUG_PRES) printf("PRES: responding-presentation-sel too large\n"); } @@ -505,7 +531,8 @@ parseNormalModeParameters(IsoPresentation* self, uint8_t* buffer, int totalLengt } } - if (hasUserData == false) { + if (hasUserData == false) + { if (DEBUG_PRES) printf("PRES: user-data is missing\n"); @@ -525,7 +552,8 @@ IsoPresentation_parseAcceptMessage(IsoPresentation* self, ByteBuffer* byteBuffer uint8_t cpTag = buffer[bufPos++]; - if (cpTag != 0x31) { + if (cpTag != 0x31) + { if (DEBUG_PRES) printf("PRES: not a CPA message\n"); return 0; @@ -535,18 +563,21 @@ IsoPresentation_parseAcceptMessage(IsoPresentation* self, ByteBuffer* byteBuffer bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: Invalid message\n"); return 0; } - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: wrong parameter length\n"); return 0; @@ -559,7 +590,8 @@ IsoPresentation_parseAcceptMessage(IsoPresentation* self, ByteBuffer* byteBuffer case 0xa2: /* normal-mode-parameters */ bufPos = parseNormalModeParameters(self, buffer, len, bufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: error parsing normal-mode-parameters\n"); return 0; @@ -594,7 +626,7 @@ IsoPresentation_createUserData(IsoPresentation* self, BufferChain writeBuffer, B int payloadLength = payload->length; int userDataLengthFieldSize = BerEncoder_determineLengthSize(payloadLength); - ; + int pdvListLength = payloadLength + (userDataLengthFieldSize + 4); int pdvListLengthFieldSize = BerEncoder_determineLengthSize(pdvListLength); @@ -624,7 +656,7 @@ IsoPresentation_createUserDataACSE(IsoPresentation* self, BufferChain writeBuffe int payloadLength = payload->length; int userDataLengthFieldSize = BerEncoder_determineLengthSize(payloadLength); - ; + int pdvListLength = payloadLength + (userDataLengthFieldSize + 4); int pdvListLengthFieldSize = BerEncoder_determineLengthSize(pdvListLength); @@ -664,7 +696,8 @@ IsoPresentation_parseUserData(IsoPresentation* self, ByteBuffer* readBuffer) bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: invalid message!\n"); return 0; @@ -675,25 +708,29 @@ IsoPresentation_parseUserData(IsoPresentation* self, ByteBuffer* readBuffer) bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: invalid message!\n"); return 0; } - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; uint8_t lenField = buffer[bufPos]; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: wrong parameter length\n"); return 0; } - switch (tag) { + switch (tag) + { case 0x02: /* abstract-syntax-name */ self->nextContextId = buffer[bufPos]; hasAbstractSyntaxName = true; @@ -703,8 +740,10 @@ IsoPresentation_parseUserData(IsoPresentation* self, ByteBuffer* readBuffer) case 0x06: /* transfer-syntax-name */ { /* check if basic-encoding (2.1.1 - 51 01) */ - if ((buffer[bufPos] != 0x51) || (buffer[bufPos + 1] != 0x01)) { - if (DEBUG_PRES) { + if ((buffer[bufPos] != 0x51) || (buffer[bufPos + 1] != 0x01)) + { + if (DEBUG_PRES) + { printf("PRES: unknown transfer-syntax-name\n"); } @@ -715,10 +754,10 @@ IsoPresentation_parseUserData(IsoPresentation* self, ByteBuffer* readBuffer) } break; - - case 0xa0: /* presentation data */ + case 0xa0: /* presentation data */ { - if (hasAbstractSyntaxName == false) { + if (hasAbstractSyntaxName == false) + { if (DEBUG_PRES) printf("PRES: abstract-syntax-name missing!\n"); @@ -752,7 +791,8 @@ IsoPresentation_parseConnect(IsoPresentation* self, ByteBuffer* byteBuffer) uint8_t cpTag = buffer[bufPos++]; - if (cpTag != 0x31) { + if (cpTag != 0x31) + { if (DEBUG_PRES) printf("PRES: not a CP type\n"); return 0; @@ -762,7 +802,8 @@ IsoPresentation_parseConnect(IsoPresentation* self, ByteBuffer* byteBuffer) bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: invalid message!\n"); return 0; @@ -771,21 +812,25 @@ IsoPresentation_parseConnect(IsoPresentation* self, ByteBuffer* byteBuffer) if (DEBUG_PRES) printf("PRES: CPType with len %i\n", len); - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: invalid message!\n"); return 0; } - switch (tag) { + switch (tag) + { case 0xa0: /* mode-selection */ { - if (buffer[bufPos++] != 0x80) { + if (buffer[bufPos++] != 0x80) + { if (DEBUG_PRES) printf("PRES: mode-value of wrong type!\n"); return 0; @@ -793,7 +838,8 @@ IsoPresentation_parseConnect(IsoPresentation* self, ByteBuffer* byteBuffer) bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: invalid message!\n"); return 0; @@ -810,12 +856,14 @@ IsoPresentation_parseConnect(IsoPresentation* self, ByteBuffer* byteBuffer) case 0xa2: /* normal-mode-parameters */ bufPos = parseNormalModeParameters(self, buffer, len, bufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_PRES) printf("PRES: error parsing normal-mode-parameters\n"); return 0; } - else { + else + { hasNormalModeParameters = true; } @@ -830,7 +878,8 @@ IsoPresentation_parseConnect(IsoPresentation* self, ByteBuffer* byteBuffer) } } - if (hasNormalModeParameters == false) { + if (hasNormalModeParameters == false) + { if (DEBUG_PRES) printf("PRES: error - normal mode parameters are missing\n");