Matter SDK Coverage Report
Current view: top level - lib/core - OTAImageHeader.cpp (source / functions) Coverage Total Hit
Test: SHA:f1767a8b0a3778fdf31b1d979afbdf544892fd94 Lines: 97.4 % 76 74
Test Date: 2026-06-03 07:35:21 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2022 Project CHIP Authors
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : #include <lib/core/OTAImageHeader.h>
      18              : 
      19              : #include <lib/core/CHIPError.h>
      20              : #include <lib/core/Optional.h>
      21              : #include <lib/core/TLVReader.h>
      22              : #include <lib/core/TLVTags.h>
      23              : #include <lib/core/TLVTypes.h>
      24              : #include <lib/support/BufferReader.h>
      25              : #include <lib/support/CodeUtils.h>
      26              : #include <lib/support/ScopedMemoryBuffer.h>
      27              : #include <lib/support/Span.h>
      28              : 
      29              : #include <string.h>
      30              : 
      31              : namespace chip {
      32              : 
      33              : namespace {
      34              : 
      35              : enum class Tag : uint8_t
      36              : {
      37              :     kVendorId              = 0,
      38              :     kProductId             = 1,
      39              :     kSoftwareVersion       = 2,
      40              :     kSoftwareVersionString = 3,
      41              :     kPayloadSize           = 4,
      42              :     kMinApplicableVersion  = 5,
      43              :     kMaxApplicableVersion  = 6,
      44              :     kReleaseNotesURL       = 7,
      45              :     kImageDigestType       = 8,
      46              :     kImageDigest           = 9,
      47              : };
      48              : 
      49              : /// Length of the fixed portion of the Matter OTA image header: FileIdentifier (4B), TotalSize (8B) and HeaderSize (4B)
      50              : constexpr uint32_t kFixedHeaderSize = 16;
      51              : 
      52              : /// Maximum supported Matter OTA image header size
      53              : constexpr uint32_t kMaxHeaderSize = 1024;
      54              : 
      55              : /// Maximum size of the software version string
      56              : constexpr size_t kMaxSoftwareVersionStringSize = 64;
      57              : 
      58              : /// Maximum size of the release notes URL
      59              : constexpr size_t kMaxReleaseNotesURLSize = 256;
      60              : 
      61              : } // namespace
      62              : 
      63            9 : void OTAImageHeaderParser::Init()
      64              : {
      65            9 :     mState         = State::kInitialized;
      66            9 :     mBufferOffset  = 0;
      67            9 :     mHeaderTlvSize = 0;
      68            9 :     mBuffer.Alloc(kFixedHeaderSize);
      69            9 : }
      70              : 
      71            2 : void OTAImageHeaderParser::Clear()
      72              : {
      73            2 :     mState         = State::kNotInitialized;
      74            2 :     mBufferOffset  = 0;
      75            2 :     mHeaderTlvSize = 0;
      76            2 :     mBuffer.Free();
      77            2 : }
      78              : 
      79          112 : CHIP_ERROR OTAImageHeaderParser::AccumulateAndDecode(ByteSpan & buffer, OTAImageHeader & header)
      80              : {
      81          112 :     CHIP_ERROR error = CHIP_NO_ERROR;
      82              : 
      83              :     // Init() may have left mBuffer null on alloc failure.
      84          112 :     if (mState != State::kNotInitialized && mBuffer.Get() == nullptr)
      85              :     {
      86            0 :         Clear();
      87            0 :         return CHIP_ERROR_NO_MEMORY;
      88              :     }
      89              : 
      90          112 :     if (mState == State::kInitialized)
      91              :     {
      92           24 :         Append(buffer, kFixedHeaderSize - mBufferOffset);
      93           24 :         error = DecodeFixed();
      94              :     }
      95              : 
      96          112 :     if (mState == State::kTlv)
      97              :     {
      98           95 :         Append(buffer, mHeaderTlvSize - mBufferOffset);
      99           95 :         error = DecodeTlv(header);
     100              :     }
     101              : 
     102          331 :     if (error != CHIP_NO_ERROR && error != CHIP_ERROR_BUFFER_TOO_SMALL)
     103              :     {
     104            2 :         Clear();
     105              :     }
     106              : 
     107          112 :     return error;
     108              : }
     109              : 
     110          119 : void OTAImageHeaderParser::Append(ByteSpan & buffer, uint32_t numBytes)
     111              : {
     112          119 :     numBytes = std::min(numBytes, static_cast<uint32_t>(buffer.size()));
     113          119 :     memcpy(&mBuffer[mBufferOffset], buffer.data(), numBytes);
     114          119 :     mBufferOffset += numBytes;
     115          119 :     buffer = buffer.SubSpan(numBytes);
     116          119 : }
     117              : 
     118           24 : CHIP_ERROR OTAImageHeaderParser::DecodeFixed()
     119              : {
     120           24 :     VerifyOrReturnError(mBufferOffset >= kFixedHeaderSize, CHIP_ERROR_BUFFER_TOO_SMALL);
     121              : 
     122            8 :     Encoding::LittleEndian::Reader reader(mBuffer.Get(), mBufferOffset);
     123              :     uint32_t fileIdentifier;
     124              :     uint64_t totalSize;
     125            8 :     ReturnErrorOnFailure(reader.Read32(&fileIdentifier).Read64(&totalSize).Read32(&mHeaderTlvSize).StatusCode());
     126            8 :     VerifyOrReturnError(fileIdentifier == kOTAImageFileIdentifier, CHIP_ERROR_INVALID_FILE_IDENTIFIER);
     127              :     // Safety check against malicious headers.
     128            7 :     VerifyOrReturnError(mHeaderTlvSize <= kMaxHeaderSize, CHIP_ERROR_NO_MEMORY);
     129            7 :     VerifyOrReturnError(mBuffer.Alloc(mHeaderTlvSize), CHIP_ERROR_NO_MEMORY);
     130              : 
     131            7 :     mState        = State::kTlv;
     132            7 :     mBufferOffset = 0;
     133              : 
     134            7 :     return CHIP_NO_ERROR;
     135              : }
     136              : 
     137           95 : CHIP_ERROR OTAImageHeaderParser::DecodeTlv(OTAImageHeader & header)
     138              : {
     139           95 :     VerifyOrReturnError(mBufferOffset >= mHeaderTlvSize, CHIP_ERROR_BUFFER_TOO_SMALL);
     140              : 
     141            6 :     TLV::TLVReader tlvReader;
     142            6 :     tlvReader.Init(mBuffer.Get(), mBufferOffset);
     143            6 :     ReturnErrorOnFailure(tlvReader.Next(TLV::TLVType::kTLVType_Structure, TLV::AnonymousTag()));
     144              : 
     145              :     TLV::TLVType outerType;
     146            6 :     ReturnErrorOnFailure(tlvReader.EnterContainer(outerType));
     147              : 
     148            6 :     ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kVendorId)));
     149            5 :     ReturnErrorOnFailure(tlvReader.Get(header.mVendorId));
     150            5 :     ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kProductId)));
     151            5 :     ReturnErrorOnFailure(tlvReader.Get(header.mProductId));
     152            5 :     ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kSoftwareVersion)));
     153            5 :     ReturnErrorOnFailure(tlvReader.Get(header.mSoftwareVersion));
     154            5 :     ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kSoftwareVersionString)));
     155            5 :     ReturnErrorOnFailure(tlvReader.Get(header.mSoftwareVersionString));
     156            5 :     VerifyOrReturnError(header.mSoftwareVersionString.size() <= kMaxSoftwareVersionStringSize, CHIP_ERROR_INVALID_STRING_LENGTH);
     157            5 :     ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kPayloadSize)));
     158            5 :     ReturnErrorOnFailure(tlvReader.Get(header.mPayloadSize));
     159            5 :     ReturnErrorOnFailure(tlvReader.Next());
     160              : 
     161            5 :     if (tlvReader.GetTag() == TLV::ContextTag(Tag::kMinApplicableVersion))
     162              :     {
     163            4 :         ReturnErrorOnFailure(tlvReader.Get(header.mMinApplicableVersion.Emplace()));
     164            4 :         ReturnErrorOnFailure(tlvReader.Next());
     165              :     }
     166              : 
     167            5 :     if (tlvReader.GetTag() == TLV::ContextTag(Tag::kMaxApplicableVersion))
     168              :     {
     169            4 :         ReturnErrorOnFailure(tlvReader.Get(header.mMaxApplicableVersion.Emplace()));
     170            4 :         ReturnErrorOnFailure(tlvReader.Next());
     171              :     }
     172              : 
     173            5 :     if (tlvReader.GetTag() == TLV::ContextTag(Tag::kReleaseNotesURL))
     174              :     {
     175            4 :         ReturnErrorOnFailure(tlvReader.Get(header.mReleaseNotesURL));
     176            4 :         VerifyOrReturnError(header.mReleaseNotesURL.size() <= kMaxReleaseNotesURLSize, CHIP_ERROR_INVALID_STRING_LENGTH);
     177            4 :         ReturnErrorOnFailure(tlvReader.Next());
     178              :     }
     179              : 
     180            5 :     ReturnErrorOnFailure(tlvReader.Expect(TLV::ContextTag(Tag::kImageDigestType)));
     181            5 :     ReturnErrorOnFailure(tlvReader.Get(header.mImageDigestType));
     182            5 :     ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kImageDigest)));
     183            5 :     ReturnErrorOnFailure(tlvReader.Get(header.mImageDigest));
     184              : 
     185            5 :     ReturnErrorOnFailure(tlvReader.ExitContainer(outerType));
     186              : 
     187            5 :     return CHIP_NO_ERROR;
     188              : }
     189              : 
     190              : } // namespace chip
        

Generated by: LCOV version 2.0-1