mirror of
https://github.com/espressif/openthread.git
synced 2026-06-06 05:24:51 +00:00
[lowpan] limit recursion depth in 6LoWPAN decompression (#12669)
This commit introduces a maximum recursion depth limit for 6LoWPAN decompression to prevent potential stack exhaustion from maliciously crafted frames with deep IPv6-in-IPv6 encapsulation. - Added a private constant kMaxRecursionDepth in the Lowpan class to define the maximum allowed recursion depth. - Updated Lowpan::Decompress() to track and validate the current recursion depth, returning kErrorParse if the limit is exceeded. - Added a new unit test TestLowpanDecompressRecursion in tests/unit/test_lowpan.cpp to verify the recursion limit and ensure it correctly handles both excessive and legitimate encapsulation levels.
This commit is contained in:
@@ -977,7 +977,8 @@ exit:
|
||||
Error Lowpan::Decompress(Message &aMessage,
|
||||
const Mac::Addresses &aMacAddrs,
|
||||
FrameData &aFrameData,
|
||||
uint16_t aDatagramLength)
|
||||
uint16_t aDatagramLength,
|
||||
uint8_t aRecursionDepth)
|
||||
{
|
||||
Error error = kErrorParse;
|
||||
Ip6::Header ip6Header;
|
||||
@@ -985,6 +986,8 @@ Error Lowpan::Decompress(Message &aMessage,
|
||||
uint16_t ip6PayloadLength;
|
||||
uint16_t currentOffset = aMessage.GetOffset();
|
||||
|
||||
VerifyOrExit(aRecursionDepth <= kMaxRecursionDepth);
|
||||
|
||||
SuccessOrExit(DecompressBaseHeader(ip6Header, compressed, aMacAddrs, aFrameData));
|
||||
|
||||
SuccessOrExit(aMessage.Append(ip6Header));
|
||||
@@ -1005,7 +1008,7 @@ Error Lowpan::Decompress(Message &aMessage,
|
||||
|
||||
aFrameData.SkipOver(sizeof(uint8_t));
|
||||
|
||||
SuccessOrExit(Decompress(aMessage, aMacAddrs, aFrameData, aDatagramLength));
|
||||
SuccessOrExit(Decompress(aMessage, aMacAddrs, aFrameData, aDatagramLength, aRecursionDepth + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -199,7 +199,8 @@ public:
|
||||
Error Decompress(Message &aMessage,
|
||||
const Mac::Addresses &aMacAddrs,
|
||||
FrameData &aFrameData,
|
||||
uint16_t aDatagramLength);
|
||||
uint16_t aDatagramLength,
|
||||
uint8_t aRecursionDepth = 0);
|
||||
|
||||
/**
|
||||
* Decompresses a LOWPAN_IPHC header.
|
||||
@@ -254,6 +255,8 @@ public:
|
||||
void MarkCompressedEcn(Message &aMessage, uint16_t aOffset);
|
||||
|
||||
private:
|
||||
static constexpr uint8_t kMaxRecursionDepth = 4;
|
||||
|
||||
static constexpr uint16_t kHcDispatch = 3 << 13;
|
||||
static constexpr uint16_t kHcDispatchMask = 7 << 13;
|
||||
|
||||
|
||||
@@ -2087,6 +2087,83 @@ void TestLowpanFragmentHeader(void)
|
||||
VerifyOrQuit(frameData.GetBytes() == frame);
|
||||
}
|
||||
|
||||
void TestLowpanDecompressRecursion(void)
|
||||
{
|
||||
Instance *instance = testInitInstance();
|
||||
Lowpan::Lowpan &lowpan = instance->Get<Lowpan::Lowpan>();
|
||||
MessagePool &messagePool = instance->Get<MessagePool>();
|
||||
|
||||
Mac::Addresses macAddrs;
|
||||
macAddrs.mSource.SetShort(0x1234);
|
||||
macAddrs.mDestination.SetShort(0x5678);
|
||||
|
||||
printf("\n=== Test name: Lowpan Decompress Recursion ===\n\n");
|
||||
|
||||
// Case 1: Excessive recursion (41 levels) -> Should fail with kErrorParse
|
||||
{
|
||||
uint8_t payload[256];
|
||||
uint16_t length = 0;
|
||||
|
||||
for (int i = 0; i < 41; i++)
|
||||
{
|
||||
payload[length++] = 0x7e;
|
||||
payload[length++] = 0x33;
|
||||
payload[length++] = 0xee;
|
||||
}
|
||||
payload[length++] = 0x7a;
|
||||
payload[length++] = 0x33;
|
||||
payload[length++] = 0x11;
|
||||
|
||||
Message *message = messagePool.Allocate(Message::kTypeIp6);
|
||||
VerifyOrQuit(message != nullptr, "Message::Allocate failed");
|
||||
|
||||
FrameData frameData;
|
||||
frameData.Init(payload, length);
|
||||
|
||||
printf("Case 1: Excessive recursion (41 levels)...\n");
|
||||
Error error = lowpan.Decompress(*message, macAddrs, frameData, 0);
|
||||
printf("Decompression returned error: %s\n", ErrorToString(error));
|
||||
VerifyOrQuit(error == kErrorParse, "Case 1 failed: should have returned kErrorParse");
|
||||
|
||||
message->Free();
|
||||
}
|
||||
|
||||
// Case 2: Legitimate recursion (2 levels) -> Should succeed
|
||||
{
|
||||
uint8_t payload[256];
|
||||
uint16_t length = 0;
|
||||
|
||||
// Level 0
|
||||
payload[length++] = 0x7e;
|
||||
payload[length++] = 0x33;
|
||||
payload[length++] = 0xee;
|
||||
// Level 1
|
||||
payload[length++] = 0x7e;
|
||||
payload[length++] = 0x33;
|
||||
payload[length++] = 0xee;
|
||||
// Level 2 (Terminal)
|
||||
payload[length++] = 0x7a;
|
||||
payload[length++] = 0x33;
|
||||
payload[length++] = 0x11;
|
||||
|
||||
Message *message = messagePool.Allocate(Message::kTypeIp6);
|
||||
VerifyOrQuit(message != nullptr, "Message::Allocate failed");
|
||||
|
||||
FrameData frameData;
|
||||
frameData.Init(payload, length);
|
||||
|
||||
printf("Case 2: Legitimate recursion (2 levels)...\n");
|
||||
Error error = lowpan.Decompress(*message, macAddrs, frameData, 0);
|
||||
printf("Decompression returned error: %s\n", ErrorToString(error));
|
||||
VerifyOrQuit(error == kErrorNone, "Case 2 failed: should have returned kErrorNone");
|
||||
|
||||
message->Free();
|
||||
}
|
||||
|
||||
testFreeInstance(instance);
|
||||
printf("PASS\n\n");
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
@@ -2094,6 +2171,7 @@ int main(void)
|
||||
ot::TestLowpanIphc();
|
||||
ot::TestLowpanMeshHeader();
|
||||
ot::TestLowpanFragmentHeader();
|
||||
ot::TestLowpanDecompressRecursion();
|
||||
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
|
||||
@@ -259,6 +259,8 @@ public:
|
||||
const char *mTestName;
|
||||
};
|
||||
|
||||
void TestLowpanDecompressRecursion(void);
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#endif // OT_UNIT_TEST_LOWPAN_HPP_
|
||||
|
||||
Reference in New Issue
Block a user