Skip to content

Commit 1074828

Browse files
bzbarsky-applepull[bot]
authored andcommitted
Make sure we handle BUSY responses during CASE properly. (#28901)
If we got a BUSY response during a CASE handshake and successfully read the wait time, we would treat that as a success case, not a failure case, and not realize that our exchange has been closed. That could lead to use-after-free when we later tried to abort an already-closed exchange. The problem was introduced in 59a0b2f (PR #28153). The new unit test fails (both with ASAN failures and with incorrect state because the client that got BUSY does not think the handshake failed) without this fix.
1 parent 6ccb000 commit 1074828

File tree

2 files changed

+83
-25
lines changed

2 files changed

+83
-25
lines changed

src/protocols/secure_channel/PairingSession.h

+20-24
Original file line numberDiff line numberDiff line change
@@ -162,44 +162,40 @@ class DLL_EXPORT PairingSession : public SessionDelegate
162162
CHIP_ERROR HandleStatusReport(System::PacketBufferHandle && msg, bool successExpected)
163163
{
164164
Protocols::SecureChannel::StatusReport report;
165-
CHIP_ERROR err = report.Parse(std::move(msg));
166-
ReturnErrorOnFailure(err);
165+
ReturnErrorOnFailure(report.Parse(std::move(msg)));
167166
VerifyOrReturnError(report.GetProtocolId() == Protocols::SecureChannel::Id, CHIP_ERROR_INVALID_ARGUMENT);
168167

169168
if (report.GetGeneralCode() == Protocols::SecureChannel::GeneralStatusCode::kSuccess &&
170169
report.GetProtocolCode() == Protocols::SecureChannel::kProtocolCodeSuccess && successExpected)
171170
{
172171
OnSuccessStatusReport();
172+
return CHIP_NO_ERROR;
173173
}
174-
else
175-
{
176-
err = OnFailureStatusReport(report.GetGeneralCode(), report.GetProtocolCode());
177174

178-
if (report.GetGeneralCode() == Protocols::SecureChannel::GeneralStatusCode::kBusy &&
179-
report.GetProtocolCode() == Protocols::SecureChannel::kProtocolCodeBusy)
175+
if (report.GetGeneralCode() == Protocols::SecureChannel::GeneralStatusCode::kBusy &&
176+
report.GetProtocolCode() == Protocols::SecureChannel::kProtocolCodeBusy)
177+
{
178+
if (!report.GetProtocolData().IsNull())
180179
{
181-
if (!report.GetProtocolData().IsNull())
180+
Encoding::LittleEndian::Reader reader(report.GetProtocolData()->Start(), report.GetProtocolData()->DataLength());
181+
182+
uint16_t minimumWaitTime = 0;
183+
CHIP_ERROR waitTimeErr = reader.Read16(&minimumWaitTime).StatusCode();
184+
if (waitTimeErr != CHIP_NO_ERROR)
185+
{
186+
ChipLogError(SecureChannel, "Failed to read the minimum wait time: %" CHIP_ERROR_FORMAT, waitTimeErr.Format());
187+
}
188+
else
182189
{
183-
Encoding::LittleEndian::Reader reader(report.GetProtocolData()->Start(),
184-
report.GetProtocolData()->DataLength());
185-
186-
uint16_t minimumWaitTime = 0;
187-
err = reader.Read16(&minimumWaitTime).StatusCode();
188-
if (err != CHIP_NO_ERROR)
189-
{
190-
ChipLogError(SecureChannel, "Failed to read the minimum wait time: %" CHIP_ERROR_FORMAT, err.Format());
191-
}
192-
else
193-
{
194-
// TODO: CASE: Notify minimum wait time to clients on receiving busy status report #28290
195-
ChipLogProgress(SecureChannel, "Received busy status report with minimum wait time: %u ms",
196-
minimumWaitTime);
197-
}
190+
// TODO: CASE: Notify minimum wait time to clients on receiving busy status report #28290
191+
ChipLogProgress(SecureChannel, "Received busy status report with minimum wait time: %u ms", minimumWaitTime);
198192
}
199193
}
200194
}
201195

202-
return err;
196+
// It's very important that we propagate the return value from
197+
// OnFailureStatusReport out to the caller. Make sure we return it directly.
198+
return OnFailureStatusReport(report.GetGeneralCode(), report.GetProtocolCode());
203199
}
204200

205201
/**

src/protocols/secure_channel/tests/TestCASESession.cpp

+63-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,14 @@ CHIP_ERROR InitFabricTable(chip::FabricTable & fabricTable, chip::TestPersistent
122122
class TestCASESecurePairingDelegate : public SessionEstablishmentDelegate
123123
{
124124
public:
125-
void OnSessionEstablishmentError(CHIP_ERROR error) override { mNumPairingErrors++; }
125+
void OnSessionEstablishmentError(CHIP_ERROR error) override
126+
{
127+
mNumPairingErrors++;
128+
if (error == CHIP_ERROR_BUSY)
129+
{
130+
mNumBusyResponses++;
131+
}
132+
}
126133

127134
void OnSessionEstablished(const SessionHandle & session) override
128135
{
@@ -137,6 +144,7 @@ class TestCASESecurePairingDelegate : public SessionEstablishmentDelegate
137144
// TODO: Rename mNumPairing* to mNumEstablishment*
138145
uint32_t mNumPairingErrors = 0;
139146
uint32_t mNumPairingComplete = 0;
147+
uint32_t mNumBusyResponses = 0;
140148
};
141149

142150
class TestOperationalKeystore : public chip::Crypto::OperationalKeystore
@@ -314,6 +322,7 @@ class TestCASESession
314322
static void SecurePairingStartTest(nlTestSuite * inSuite, void * inContext);
315323
static void SecurePairingHandshakeTest(nlTestSuite * inSuite, void * inContext);
316324
static void SecurePairingHandshakeServerTest(nlTestSuite * inSuite, void * inContext);
325+
static void ClientReceivesBusyTest(nlTestSuite * inSuite, void * inContext);
317326
static void Sigma1ParsingTest(nlTestSuite * inSuite, void * inContext);
318327
static void DestinationIdTest(nlTestSuite * inSuite, void * inContext);
319328
static void SessionResumptionStorage(nlTestSuite * inSuite, void * inContext);
@@ -536,6 +545,58 @@ void TestCASESession::SecurePairingHandshakeServerTest(nlTestSuite * inSuite, vo
536545

537546
chip::Platform::Delete(pairingCommissioner);
538547
chip::Platform::Delete(pairingCommissioner1);
548+
549+
gPairingServer.Shutdown();
550+
}
551+
552+
void TestCASESession::ClientReceivesBusyTest(nlTestSuite * inSuite, void * inContext)
553+
{
554+
TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);
555+
TemporarySessionManager sessionManager(inSuite, ctx);
556+
557+
TestCASESecurePairingDelegate delegateCommissioner1, delegateCommissioner2;
558+
CASESession pairingCommissioner1, pairingCommissioner2;
559+
560+
pairingCommissioner1.SetGroupDataProvider(&gCommissionerGroupDataProvider);
561+
pairingCommissioner2.SetGroupDataProvider(&gCommissionerGroupDataProvider);
562+
563+
auto & loopback = ctx.GetLoopback();
564+
loopback.mSentMessageCount = 0;
565+
566+
NL_TEST_ASSERT(inSuite,
567+
gPairingServer.ListenForSessionEstablishment(&ctx.GetExchangeManager(), &ctx.GetSecureSessionManager(),
568+
&gDeviceFabrics, nullptr, nullptr,
569+
&gDeviceGroupDataProvider) == CHIP_NO_ERROR);
570+
571+
ExchangeContext * contextCommissioner1 = ctx.NewUnauthenticatedExchangeToBob(&pairingCommissioner1);
572+
ExchangeContext * contextCommissioner2 = ctx.NewUnauthenticatedExchangeToBob(&pairingCommissioner2);
573+
574+
NL_TEST_ASSERT(inSuite,
575+
pairingCommissioner1.EstablishSession(sessionManager, &gCommissionerFabrics,
576+
ScopedNodeId{ Node01_01, gCommissionerFabricIndex }, contextCommissioner1,
577+
nullptr, nullptr, &delegateCommissioner1, NullOptional) == CHIP_NO_ERROR);
578+
NL_TEST_ASSERT(inSuite,
579+
pairingCommissioner2.EstablishSession(sessionManager, &gCommissionerFabrics,
580+
ScopedNodeId{ Node01_01, gCommissionerFabricIndex }, contextCommissioner2,
581+
nullptr, nullptr, &delegateCommissioner2, NullOptional) == CHIP_NO_ERROR);
582+
583+
ServiceEvents(ctx);
584+
585+
// We should have one full handshake and one Sigma1 + Busy + ack. If that
586+
// ever changes (e.g. because our server starts supporting multiple parallel
587+
// handshakes), this test needs to be fixed so that the server is still
588+
// responding BUSY to the client.
589+
NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == sTestCaseMessageCount + 3);
590+
NL_TEST_ASSERT(inSuite, delegateCommissioner1.mNumPairingComplete == 1);
591+
NL_TEST_ASSERT(inSuite, delegateCommissioner2.mNumPairingComplete == 0);
592+
593+
NL_TEST_ASSERT(inSuite, delegateCommissioner1.mNumPairingErrors == 0);
594+
NL_TEST_ASSERT(inSuite, delegateCommissioner2.mNumPairingErrors == 1);
595+
596+
NL_TEST_ASSERT(inSuite, delegateCommissioner1.mNumBusyResponses == 0);
597+
NL_TEST_ASSERT(inSuite, delegateCommissioner2.mNumBusyResponses == 1);
598+
599+
gPairingServer.Shutdown();
539600
}
540601

541602
struct Sigma1Params
@@ -1115,6 +1176,7 @@ static const nlTest sTests[] =
11151176
NL_TEST_DEF("Start", chip::TestCASESession::SecurePairingStartTest),
11161177
NL_TEST_DEF("Handshake", chip::TestCASESession::SecurePairingHandshakeTest),
11171178
NL_TEST_DEF("ServerHandshake", chip::TestCASESession::SecurePairingHandshakeServerTest),
1179+
NL_TEST_DEF("ClientReceivesBusy", chip::TestCASESession::ClientReceivesBusyTest),
11181180
NL_TEST_DEF("Sigma1Parsing", chip::TestCASESession::Sigma1ParsingTest),
11191181
NL_TEST_DEF("DestinationId", chip::TestCASESession::DestinationIdTest),
11201182
NL_TEST_DEF("SessionResumptionStorage", chip::TestCASESession::SessionResumptionStorage),

0 commit comments

Comments
 (0)