[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.
This commit is contained in:
Jonathan Hui
2026-05-07 07:31:04 -07:00
committed by GitHub
parent 772ddb9802
commit 3d731aae2f
2 changed files with 55 additions and 4 deletions
+11 -1
View File
@@ -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()));
+44 -3
View File
@@ -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<Coap::ApplicationCoap>().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<Coap::ApplicationCoap>().SendMessageWithResponseHandlerSeparateParams(
*message, messageInfo, nullptr, &HandleReproductionResponse, nullptr, nullptr, nullptr));
nexus.AdvanceTime(5 * 1000);
VerifyOrQuit(sReproductionResponseReceived);
VerifyOrQuit(sReproductionResponseCode == OT_COAP_CODE_REQUEST_INCOMPLETE);
leader.Get<Coap::ApplicationCoap>().RemoveBlockWiseResource(resource);
IgnoreError(leader.Get<Coap::ApplicationCoap>().Stop());
IgnoreError(router.Get<Coap::ApplicationCoap>().Stop());