Skip to content

Commit 627d9bb

Browse files
Hein Tiboscharchigup
andcommitted
Fix heap OOB read in prvProcessICMPEchoReply()
prvProcessICMPEchoReply() subtracted ipSIZE_OF_IPv4_HEADER and ipSIZE_OF_ICMPv4_HEADER from the IP header's usLength field without first validating that usLength was large enough. When usLength < 28, the uint16_t arithmetic wraps around, producing a data length of up to 65508, causing the verification loop to read far beyond the allocated network buffer. Fix by: - Passing the NetworkBufferDescriptor_t to get access to xDataLength - Validating both the buffer size and IP-declared length are at least large enough to contain the required headers before subtracting - Ensuring the IP-declared payload does not exceed the available buffer space (usByteCount <= usByteAvail) Co-authored-by: Archit Gupta <archigup@amazon.com>
1 parent ef50082 commit 627d9bb

2 files changed

Lines changed: 48 additions & 29 deletions

File tree

source/FreeRTOS_ICMP.c

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
* vApplicationPingReplyHook() is called with the results.
6666
*/
6767
#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 )
68-
static void prvProcessICMPEchoReply( ICMPPacket_t * const pxICMPPacket );
68+
static void prvProcessICMPEchoReply( const NetworkBufferDescriptor_t * const pxNetworkBuffer );
6969
#endif /* ipconfigSUPPORT_OUTGOING_PINGS */
7070

7171
#if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 )
@@ -110,7 +110,7 @@
110110
case ipICMP_ECHO_REPLY:
111111
#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 )
112112
{
113-
prvProcessICMPEchoReply( pxICMPPacket );
113+
prvProcessICMPEchoReply( pxNetworkBuffer );
114114
}
115115
#endif /* ipconfigSUPPORT_OUTGOING_PINGS */
116116
break;
@@ -201,46 +201,67 @@
201201
/**
202202
* @brief Process an ICMP echo reply.
203203
*
204-
* @param[in] pxICMPPacket The IP packet that contains the ICMP message.
204+
* @param[in] pxNetworkBuffer The network buffer descriptor containing the ICMP packet.
205205
*/
206-
static void prvProcessICMPEchoReply( ICMPPacket_t * const pxICMPPacket )
206+
static void prvProcessICMPEchoReply( const NetworkBufferDescriptor_t * const pxNetworkBuffer )
207207
{
208+
/* The packet contained an ICMPv4 frame. */
209+
/* MISRA Ref 11.3.1 [Misaligned access] */
210+
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
211+
/* coverity[misra_c_2012_rule_11_3_violation] */
212+
ICMPPacket_t * pxICMPPacket = ( ICMPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer;
208213
ePingReplyStatus_t eStatus = eSuccess;
209-
uint16_t usDataLength, usCount;
214+
uint16_t usDataLength, usByteCount, usCount;
215+
size_t usByteAvail;
210216
uint8_t * pucByte;
211217

212218
/* Find the total length of the IP packet. */
213219
usDataLength = pxICMPPacket->xIPHeader.usLength;
214-
usDataLength = FreeRTOS_ntohs( usDataLength );
220+
usByteCount = FreeRTOS_ntohs( usDataLength );
221+
usByteAvail = pxNetworkBuffer->xDataLength;
215222

216-
/* Remove the length of the IP headers to obtain the length of the ICMP
217-
* message itself. */
218-
usDataLength = ( uint16_t ) ( ( ( uint32_t ) usDataLength ) - ipSIZE_OF_IPv4_HEADER );
223+
if( usByteAvail >= ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_ICMPv4_HEADER ) )
224+
{
225+
usByteAvail = usByteAvail - ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_ICMPv4_HEADER );
219226

220-
/* Remove the length of the ICMP header, to obtain the length of
221-
* data contained in the ping. */
222-
usDataLength = ( uint16_t ) ( ( ( uint32_t ) usDataLength ) - ipSIZE_OF_ICMPv4_HEADER );
227+
if( usByteCount >= ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_ICMPv4_HEADER ) )
228+
{
229+
usByteCount = ( uint16_t ) ( usByteCount - ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_ICMPv4_HEADER ) );
223230

224-
/* Checksum has already been checked before in prvProcessIPPacket */
231+
if( usByteCount <= usByteAvail )
232+
{
233+
/* Checksum has already been checked before in prvProcessIPPacket */
225234

226-
/* Find the first byte of the data within the ICMP packet. */
227-
pucByte = ( uint8_t * ) pxICMPPacket;
228-
pucByte = &( pucByte[ sizeof( ICMPPacket_t ) ] );
235+
/* Find the first byte of the data within the ICMP packet. */
236+
pucByte = ( uint8_t * ) pxICMPPacket;
237+
/* Add an offset of 14 + 20 + 8. */
238+
pucByte = &( pucByte[ sizeof( ICMPPacket_t ) ] );
229239

230-
/* Check each byte. */
231-
for( usCount = 0; usCount < usDataLength; usCount++ )
232-
{
233-
if( *pucByte != ( uint8_t ) ipECHO_DATA_FILL_BYTE )
240+
/* Check each byte. */
241+
for( usCount = 0; usCount < usByteCount; usCount++ )
242+
{
243+
if( *pucByte != ( uint8_t ) ipECHO_DATA_FILL_BYTE )
244+
{
245+
eStatus = eInvalidData;
246+
break;
247+
}
248+
249+
pucByte++;
250+
}
251+
252+
/* Call back into the application to pass it the result. */
253+
vApplicationPingReplyHook( eStatus, pxICMPPacket->xICMPHeader.usIdentifier );
254+
}
255+
}
256+
else
234257
{
235-
eStatus = eInvalidData;
236-
break;
258+
/* usByteCount too small, drop. */
237259
}
238-
239-
pucByte++;
240260
}
241-
242-
/* Call back into the application to pass it the result. */
243-
vApplicationPingReplyHook( eStatus, pxICMPPacket->xICMPHeader.usIdentifier );
261+
else
262+
{
263+
/* Buffer too small, drop. */
264+
}
244265
}
245266

246267
#endif /* if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) */

test/unit-test/FreeRTOS_ICMP/FreeRTOS_ICMP_utest.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ void test_ProcessICMPPacket_AllZeroData( void )
8888

8989
memset( ucEthBuffer, 0, ipconfigTCP_MSS );
9090

91-
vApplicationPingReplyHook_Expect( eInvalidData, 0 );
92-
9391
eResult = ProcessICMPPacket( pxNetworkBuffer );
9492

9593
TEST_ASSERT_EQUAL( eReleaseBuffer, eResult );

0 commit comments

Comments
 (0)