From 3d731aae2ff7f3805a6868d40c765710d19bd6b1 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Thu, 7 May 2026 07:31:04 -0700 Subject: [PATCH] [coap] fix null-pointer dereference on Block2 invalid requests (#13079) This commit fixes a deterministic null-pointer dereference in CoapBase::ProcessBlock2Request when receiving a Block2 request with block number greater than 0 without a preceding active blockwise transfer. Previously, when mLastResponse was null, the option copying logic would unconditionally attempt to initialize the iterator with a dereferenced mLastResponse pointer (iterator.Init(*mLastResponse)), causing a segmentation fault crash. This fix inserts a VerifyOrExit check on mLastResponse inside ProcessBlock2Request. If mLastResponse is null, it returns the kErrorNoFrameReceived error code. In ProcessBlockwiseRequest, this is mapped to a 4.08 Request Entity Incomplete response, matching the spec-compliant error handling behavior of Block1. An automated reproduction and verification test case has also been added to tests/nexus/test_coap_block.cpp. --- src/core/coap/coap.cpp | 12 ++++++++- tests/nexus/test_coap_block.cpp | 47 ++++++++++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp index 737a51097..cd931dbb6 100644 --- a/src/core/coap/coap.cpp +++ b/src/core/coap/coap.cpp @@ -978,10 +978,18 @@ Error CoapBase::ProcessBlockwiseRequest(Msg &aRxMsg, const Message::UriPathStrin case 2: if (resource.mTransmitHook != nullptr) { - if ((error = ProcessBlock2Request(aRxMsg, resource)) != kErrorNone) + switch (ProcessBlock2Request(aRxMsg, resource)) { + case kErrorNone: + break; + case kErrorNoFrameReceived: + IgnoreError(SendResponse(kCodeRequestIncomplete, aRxMsg)); + error = kErrorDrop; + break; + default: IgnoreError(SendResponse(kCodeInternalError, aRxMsg)); error = kErrorDrop; + break; } } break; @@ -1268,6 +1276,8 @@ Error CoapBase::ProcessBlock2Request(Msg &aRxMsg, const ResourceBlockWise &aReso ExitNow(); } + VerifyOrExit(mLastResponse != nullptr, error = kErrorNoFrameReceived); + VerifyOrExit((response = NewMessage()) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = response->Init(kTypeAck, kCodeContent, aRxMsg.GetMessageId())); diff --git a/tests/nexus/test_coap_block.cpp b/tests/nexus/test_coap_block.cpp index 7bae77fd6..e5eb2c42c 100644 --- a/tests/nexus/test_coap_block.cpp +++ b/tests/nexus/test_coap_block.cpp @@ -38,9 +38,27 @@ namespace Nexus { static otError TransmitHook(void *aContext, uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore); -static bool sRequestHandlerCalled = false; -static bool sReceiveHookCalled = false; -static bool sTransmitHookCalled = false; +static bool sRequestHandlerCalled = false; +static bool sReceiveHookCalled = false; +static bool sTransmitHookCalled = false; +static bool sReproductionResponseReceived = false; +static otCoapCode sReproductionResponseCode = OT_COAP_CODE_EMPTY; + +static void HandleReproductionResponse(void *aContext, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + otError aResult) +{ + OT_UNUSED_VARIABLE(aContext); + OT_UNUSED_VARIABLE(aMessageInfo); + OT_UNUSED_VARIABLE(aResult); + + sReproductionResponseReceived = true; + if (aMessage != nullptr) + { + sReproductionResponseCode = otCoapMessageGetCode(aMessage); + } +} static void HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) { @@ -212,6 +230,29 @@ void TestCoapBlock(void) VerifyOrQuit(sTransmitHookCalled); // Router should transmit blocks VerifyOrQuit(sReceiveHookCalled); // Leader should receive blocks + // Reproduction test case: + // Router sends GET request with Block2 number 1 (NUM > 0) but without active transfer + message = router.Get().NewMessage(); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(message->Init(Coap::kTypeConfirmable, Coap::kCodeGet)); + SuccessOrQuit(message->AppendUriPathOptions("test")); + + blockInfo.mBlockNumber = 1; + blockInfo.mBlockSzx = Coap::kBlockSzx16; + blockInfo.mMoreBlocks = false; + SuccessOrQuit(message->AppendBlockOption(Coap::kOptionBlock2, blockInfo)); + + sReproductionResponseReceived = false; + sReproductionResponseCode = OT_COAP_CODE_EMPTY; + + SuccessOrQuit(router.Get().SendMessageWithResponseHandlerSeparateParams( + *message, messageInfo, nullptr, &HandleReproductionResponse, nullptr, nullptr, nullptr)); + + nexus.AdvanceTime(5 * 1000); + + VerifyOrQuit(sReproductionResponseReceived); + VerifyOrQuit(sReproductionResponseCode == OT_COAP_CODE_REQUEST_INCOMPLETE); + leader.Get().RemoveBlockWiseResource(resource); IgnoreError(leader.Get().Stop()); IgnoreError(router.Get().Stop());