mirror of
https://github.com/espressif/openthread.git
synced 2026-08-21 09:59:52 +00:00
[ip6] cap recursion depth in HandleDatagram to 4 (#13065)
This commit introduces a recursion depth limit of 4 in Ip6::HandleDatagram to prevent unbounded stack recursion from deeply nested IPv6-in-IPv6 tunnel packets (NextHeader = 41). This mirrors the safety limit fix implemented in the 6LoWPAN layer decompress path (issue #12669). A new Nexus test case `ipv6_recursion` has been added to construct and verify that packets exceeding the depth limit are correctly dropped with kErrorDrop, while valid nesting depth succeeds.
This commit is contained in:
@@ -1280,10 +1280,12 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled)
|
||||
Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled, uint8_t aRecursionDepth)
|
||||
{
|
||||
Error error;
|
||||
Header header;
|
||||
Error error;
|
||||
Header header;
|
||||
|
||||
VerifyOrExit(aRecursionDepth <= kMaxRecursionDepth, error = kErrorDrop);
|
||||
bool receive;
|
||||
bool forwardThread;
|
||||
bool forwardHost;
|
||||
@@ -1322,7 +1324,7 @@ Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled)
|
||||
|
||||
Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageReceive, *messagePtr);
|
||||
|
||||
IgnoreError(HandleDatagram(messagePtr.PassOwnership(), aIsReassembled));
|
||||
SuccessOrExit(error = HandleDatagram(messagePtr.PassOwnership(), aIsReassembled, aRecursionDepth + 1));
|
||||
|
||||
receive = false;
|
||||
forwardHost = false;
|
||||
|
||||
@@ -179,7 +179,7 @@ public:
|
||||
* @retval kErrorNoRoute No route to host.
|
||||
* @retval kErrorParse Encountered a malformed header when processing the message.
|
||||
*/
|
||||
Error HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled = false);
|
||||
Error HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled = false, uint8_t aRecursionDepth = 0);
|
||||
|
||||
/**
|
||||
* Sets the callback to provide received raw IPv6 datagrams.
|
||||
@@ -318,6 +318,7 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
static constexpr uint8_t kMaxRecursionDepth = 4;
|
||||
static constexpr uint8_t kReassemblyTimeout = OPENTHREAD_CONFIG_IP6_REASSEMBLY_TIMEOUT;
|
||||
|
||||
static constexpr uint16_t kMinimalMtu = 1280;
|
||||
|
||||
@@ -43,8 +43,9 @@ void TestIPv6Recursion(void)
|
||||
* - Leader
|
||||
*
|
||||
* Description:
|
||||
* The purpose of this test case is to validate that lowpan compression enforces
|
||||
* the kMaxRecursionDepth limit to prevent excessive recursive stack usage.
|
||||
* The purpose of this test case is to validate that both Ip6::HandleDatagram
|
||||
* and lowpan compression enforce their respective kMaxRecursionDepth limit
|
||||
* to prevent unbounded stack recursion/excessive recursive stack usage.
|
||||
*/
|
||||
|
||||
Core nexus;
|
||||
@@ -94,9 +95,42 @@ void TestIPv6Recursion(void)
|
||||
Log("Test Case 1: Passed successfully!");
|
||||
}
|
||||
|
||||
// Test Case 2: Direct call to Lowpan::Compress with depth 6 nested packet
|
||||
// Test Case 2: Recursion depth 6 (exceeds limit of 4 in Ip6::HandleDatagram)
|
||||
{
|
||||
Log("Test Case 2: Call Lowpan::Compress with depth 6 nested packet (should cap and fall back gracefully)");
|
||||
Log("Test Case 2: Send nested IPv6 packet with depth 6 (exceeds limit)");
|
||||
Message *message = leader.Get<Ip6::Ip6>().NewMessage();
|
||||
VerifyOrQuit(message != nullptr);
|
||||
|
||||
// Construct 6 nested headers:
|
||||
// - i = 5 (innermost): NH = kProtoNone, PayloadLen = 0
|
||||
// - i = 0 to 4: NH = kProtoIp6, PayloadLen = (5 - i) * 40
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
Ip6::Header header;
|
||||
header.InitVersionTrafficClassFlow();
|
||||
header.SetSource(selfAddress);
|
||||
header.SetDestination(selfAddress);
|
||||
if (i == 5)
|
||||
{
|
||||
header.SetNextHeader(Ip6::kProtoNone);
|
||||
header.SetPayloadLength(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
header.SetNextHeader(Ip6::kProtoIp6);
|
||||
header.SetPayloadLength((5 - i) * sizeof(Ip6::Header));
|
||||
}
|
||||
SuccessOrQuit(message->Append(header));
|
||||
}
|
||||
|
||||
Error error = leader.Get<Ip6::Ip6>().HandleDatagram(OwnedPtr<Message>(message));
|
||||
VerifyOrQuit(error == kErrorDrop);
|
||||
Log("Test Case 2: Passed successfully (packet with depth 6 was dropped with kErrorDrop)!");
|
||||
}
|
||||
|
||||
// Test Case 3: Direct call to Lowpan::Compress with depth 6 nested packet
|
||||
{
|
||||
Log("Test Case 3: Call Lowpan::Compress with depth 6 nested packet (should cap and fall back gracefully)");
|
||||
Message *message = leader.Get<Ip6::Ip6>().NewMessage();
|
||||
VerifyOrQuit(message != nullptr);
|
||||
|
||||
@@ -132,7 +166,7 @@ void TestIPv6Recursion(void)
|
||||
// and emit the remaining nested headers inline as opaque payload.
|
||||
Error error = leader.Get<Lowpan::Lowpan>().Compress(*message, macAddrs, frameBuilder);
|
||||
VerifyOrQuit(error == kErrorNone);
|
||||
Log("Test Case 2: Passed successfully (returned kErrorNone without excessive stack usage)!");
|
||||
Log("Test Case 3: Passed successfully (returned kErrorNone without excessive stack usage)!");
|
||||
message->Free();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user