-
Notifications
You must be signed in to change notification settings - Fork 721
Add boundary checks to prevent memory safety issues in BgpLayer::getHeaderLen #1954
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unsure why the CI has a failure on checkout on windows. :/
I think the error is that Windows can't contain |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## dev #1954 +/- ##
==========================================
- Coverage 83.51% 83.49% -0.02%
==========================================
Files 310 310
Lines 54884 54910 +26
Branches 12220 12223 +3
==========================================
+ Hits 45834 45849 +15
- Misses 7786 8197 +411
+ Partials 1264 864 -400
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
if (m_Data - m_Packet->m_RawPacket->getRawData() + static_cast<ptrdiff_t>(offsetInLayer) > | ||
static_cast<ptrdiff_t>(m_Packet->m_RawPacket->getRawDataLen())) | ||
{ | ||
PCPP_LOG_ERROR("Requested offset is larger than total packet length"); | ||
return false; | ||
} | ||
|
||
if (m_NextLayer != nullptr && static_cast<ptrdiff_t>(offsetInLayer) > m_NextLayer->m_Data - m_Data) | ||
{ | ||
PCPP_LOG_ERROR("Requested offset exceeds current layer's boundary"); | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these checks needed? All we need to check is that offsetInLayer
is within the layer's boundary, which means it is smaller than or equal to m_DataLen
. Why do we need to compare it to the whole packet or involve m_NextLayer
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Length-related errors introduced during layer shortening can result in the same sanitizer issues when the layer is later extended
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand... can you elaborate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to say that problems with m_DataLen
not matching the actual layer length and the length from the bgp_common_header
, as in the examples below, can lead to problems with exceeding the boundaries of the layer or packet during extension.
if (m_Data - m_Packet->m_RawPacket->getRawData() + static_cast<ptrdiff_t>(offsetInLayer) + | ||
static_cast<ptrdiff_t>(numOfBytesToShorten) > | ||
static_cast<ptrdiff_t>(m_Packet->m_RawPacket->getRawDataLen())) | ||
{ | ||
PCPP_LOG_ERROR("Requested number of bytes to shorten is larger than total packet length"); | ||
return false; | ||
} | ||
|
||
if (m_NextLayer != nullptr && | ||
static_cast<ptrdiff_t>(offsetInLayer) + static_cast<ptrdiff_t>(numOfBytesToShorten) > | ||
m_NextLayer->m_Data - m_Data) | ||
{ | ||
PCPP_LOG_ERROR("Requested number of bytes to shorten exceeds current layer's boundary"); | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I analyzed several malformed packets that triggered sanitizer errors, and identified some of the root causes.
When iterating over layers during recalculation after shortening a layer, the pointer to the next layer is computed as dataPtr
(current layer pointer) + headerLen
. For BGP, headerLen
corresponds either to the length field of the bgp_common_header
structure or to the actual layer length m_DataLen
— whichever is smaller. In malformed packets, the header length may not match the actual layer length, or may contain arbitrary values (possibly due to previous incorrect shortening or extension), which can lead to several issues
-
numOfBytesToShorten
is subtracted from them_DataLen
of all layers preceding the shortened one. IfnumOfBytesToShorten
is greater thanm_DataLen
, this causesm_DataLen
to wrap around, resulting in an extremely large value. In such cases, when calculatingheaderLen
, the header field is selected, which may be invalid, leading to out-of-bounds memory access. -
In the shortened layer,
numOfBytesToShorten
is first subtracted fromm_DataLen
, then again fromheaderLen
.BgpLayer::getHeaderLen()
returns newm_DataLen
as the smallest, which results innumOfBytesToShorten
being subtracted twice, potentially producing a negative value.
I believe headerLen
calculation could be moved before updating the current layer’s m_DataLen
. However, this would not resolve the issue described in the next point.
- Malformed packets may also contain nested BGP layers, which is inherently invalid and can trigger the errors described earlier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
numOfBytesToShorten
is subtracted from them_DataLen
of all layers preceding the shortened one. IfnumOfBytesToShorten
is greater thanm_DataLen
How can numOfBytesToShorten
be greater than m_DataLen
? 🤔
- In the shortened layer,
numOfBytesToShorten
is first subtracted fromm_DataLen
, then again fromheaderLen
.BgpLayer::getHeaderLen()
returns newm_DataLen
as the smallest, which results innumOfBytesToShorten
being subtracted twice, potentially producing a negative value.
I believe this is only relevant to the changes in Packet.cpp
, right?
- Malformed packets may also contain nested BGP layers, which is inherently invalid and can trigger the errors described earlier.
By nested BPG layers, do you mean a BGP layer that comes after another BGP layer? If yes, this is actually a valid scenario that we support:
PcapPlusPlus/Packet++/src/BgpLayer.cpp
Line 80 in 32384c2
void BgpLayer::parseNextLayer() |
What's the issue in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can numOfBytesToShorten be greater than m_DataLen? 🤔
I'll try to explain with an example. The diagrams show the packet layout in memory. Each dot represents the start of a layer, layer addresses are represented by the last two bytes, and for some points I've calculated the actual offset from the beginning of the packet for clarity.
Let's consider a packet from the file *d94f8b
from regression_samples before shortening that caused sanitizer error.
The fragment that should be removed is highlighted in bold (atIndex = 148
, numOfBytesToShorten = 46
).
The first thing that stands out is that it gets to the next layer. Simply adding a check like (size_t)offsetInLayer + numOfBytesToShorten >= m_DataLen
won't be sufficient in this case, because m_DataLen
of BGP layers doesn't match the actual layer size in memory:
102 - 66 = 36, but DataLen = 95
183 - 125 = 58, but DataLen = 73
However, the error doesn't occur because of this. After removing the fragment, m_DataLen
of previous layers is recalculated according to the condition:
if (!passedExtendedLayer)
curLayer->m_DataLen -= numOfBytesToShorten;
Since the layer at address afe6
has length of 23, subtracting 46 causes an overflow. The headerLen
is taken from the bgp_common_header
structure (which is 907 in this case), dataPtr
points beyond the packet boundaries, and subsequent call to BgpLayer::getHeaderLen() results in a heap-buffer-overflow error.
By nested BPG layers, do you mean a BGP layer that comes after another BGP layer?
I mean BGP layers where one layer is inside another, i.e. in the BGP payload there is another BGP layer.
For example, in file *d6be97
the following situation occurs:

I believe this is only relevant to the changes in Packet.cpp, right?
If you mean moving the calculation of headerLen
, then yes, this applies to changes in Packet.cpp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the thorough and detailed explanation, much appreciated! 🙏
I have 2 follow-up questions:
- Since the fixes are within
extendLayer()
andshortenLayer()
I assume you found them when trying to edit packets, is that correct? As far as I know, our regression tests don't edit packets, so the files you added toTests/Fuzzers/RegressionTests/regression_samples
don't actually test the scenario being fixed - Is there any way to make these fixes inside
BgpLayer
or do they have to beLayer
andPacket
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
This PR addresses bug fix [Bug]: Heap-Use-After-Free and Heap-Buffer-Overflow in
BgpLayer::getHeaderLen()
#1864, and the files added to regression_samples are poc files that triggered sanitizer errors. I can write additional tests if needed -
As for the BGP layer, I believe the following checks could be implemented:
- Disallow nested BGP layers.
- Do not shorten the length of the BGP layers that precede the layer that is actually being shortened.
- Ensure the actual length matches the
m_DataLen
. - Update the length field in the header struct.
- Disallow BGP layers shorter than the mandatory header length (Header + Mandatory Attributes).
However, I'm not 100% sure these measures will be sufficient. There might be some combination of shorten/extend that could still lead to memory access errors. Also, shorten/extend operations with other layers could cause the same type of issues.
if (dataPtr > m_RawPacket->getRawData() + m_RawPacket->getRawDataLen()) | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can such a thing happen? curLayer->getHeaderLen()
should never exceed the packet data, unless we have a bug in one of the layers
if (dataPtr > m_RawPacket->getRawData() + m_RawPacket->getRawDataLen()) | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
Closes #1864
This PR fixes heap-use-after-free and heap-buffer-overflow issues in BgpLayer::getHeaderLen(), reported by AddressSanitizer when extending or shortening the BGP layer
Added boundary checks to ensure that requested offsets and lengths do not exceed:
Added regression_samples