From a76515b3d4216c8eaac3a0c556a05eacc05a324e Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Fri, 24 Jul 2026 08:58:14 -0700 Subject: [PATCH] [mesh-forwarder] fix priority queue iteration in EvictMessage() (#13397) When `MeshForwarder::EvictMessage()` searches for an equal-or-higher priority indirect message during buffer pool exhaustion (`kEvictReasonNoMessageBuffer`), the inner loop calls `mSendQueue.GetHeadForPriority(aPriority)` instead of `mSendQueue.GetHeadForPriority(static_cast( priority))`. If `aPriority < priority` (for example, allocating a `low` priority buffer when the send queue holds `normal` priority indirect messages), `GetHeadForPriority(aPriority)` returns the head of `aPriority` messages (`low`), immediately triggering `if (message->GetPriority() != priority) break;`. Consequently, `EvictMessage()` exits early without inspecting higher-priority indirect messages and drops the incoming packet with `kErrorNoBufs`. This commit fixes the inner loop to use `static_cast(priority)` so each priority bucket from `aPriority` up to `kNumPriorities - 1` is correctly inspected. It also adds a unit test (`TestPriorityQueueEvictIteration`) in `tests/unit/test_priority_queue.cpp` verifying equal-or-higher priority iteration in `PriorityQueue`. --- src/core/thread/mesh_forwarder_ftd.cpp | 3 +- tests/unit/test_priority_queue.cpp | 52 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index f69096f47..8c2c91e06 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -220,7 +220,8 @@ Error MeshForwarder::EvictMessage(Message::Priority aPriority, EvictReason aEvic for (uint8_t priority = aPriority; priority < Message::kNumPriorities; priority++) { // search for an equal or higher priority indirect message to evict - for (Message *message = mSendQueue.GetHeadForPriority(aPriority); message; message = message->GetNext()) + for (Message *message = mSendQueue.GetHeadForPriority(static_cast(priority)); message; + message = message->GetNext()) { if (message->GetPriority() != priority) { diff --git a/tests/unit/test_priority_queue.cpp b/tests/unit/test_priority_queue.cpp index e4a3e795c..2bf658cb4 100644 --- a/tests/unit/test_priority_queue.cpp +++ b/tests/unit/test_priority_queue.cpp @@ -697,6 +697,57 @@ void TestPriorityQueue(void) testFreeInstance(instance); } + +void TestPriorityQueueEvictIteration(void) +{ + Instance *instance; + MessagePool *messagePool; + PriorityQueue queue; + Message *lowMsg; + Message *normalMsg; + Message *foundMsg = nullptr; + + instance = testInitInstance(); + VerifyOrQuit(instance != nullptr); + + messagePool = &instance->Get(); + + lowMsg = messagePool->Allocate(Message::kTypeIp6, 0, Message::Settings(Message::kPriorityLow)); + VerifyOrQuit(lowMsg != nullptr); + normalMsg = messagePool->Allocate(Message::kTypeIp6, 0, Message::Settings(Message::kPriorityNormal)); + VerifyOrQuit(normalMsg != nullptr); + + queue.Enqueue(*lowMsg); + queue.Enqueue(*normalMsg); + + // Verify fixed inner loop iteration: GetHeadForPriority(static_cast(priority)) + for (uint8_t priority = Message::kPriorityLow; priority < Message::kNumPriorities; priority++) + { + for (Message *message = queue.GetHeadForPriority(static_cast(priority)); message; + message = message->GetNext()) + { + if (message->GetPriority() != priority) + { + break; + } + + if (message == normalMsg) + { + foundMsg = message; + break; + } + } + } + + VerifyOrQuit(foundMsg == normalMsg, "Failed to inspect equal-or-higher priority message in PriorityQueue"); + + queue.Dequeue(*lowMsg); + queue.Dequeue(*normalMsg); + lowMsg->Free(); + normalMsg->Free(); + + testFreeInstance(instance); +} // NOLINTEND(modernize-loop-convert) } // namespace ot @@ -704,6 +755,7 @@ void TestPriorityQueue(void) int main(void) { ot::TestPriorityQueue(); + ot::TestPriorityQueueEvictIteration(); printf("All tests passed\n"); return 0; }