Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/.cSpellWords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ amsdu
ANAR
ANARBMCR
ANCEN
ANCOUNT
ANEG
ANEGCAPABLE
ANEGCOMPLETE
Expand Down
8 changes: 7 additions & 1 deletion source/FreeRTOS_DNS_Parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -693,10 +693,16 @@
BaseType_t xReturn = pdTRUE;
const DNSAnswerRecord_t * pxDNSAnswerRecord;
IPv46_Address_t xIP_Address;
uint16_t usMaxAnswers = pxSet->usAnswers;

struct freertos_addrinfo * pxNewAddress = NULL;

for( x = 0U; x < pxSet->usAnswers; x++ )
if( usMaxAnswers > ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY )
{
usMaxAnswers = ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY;
}

for( x = 0U; x < usMaxAnswers; x++ )
{
BaseType_t xDoAccept = pdFALSE;

Expand Down
51 changes: 51 additions & 0 deletions test/unit-test/FreeRTOS_DNS_Parser/FreeRTOS_DNS_Parser_utest.c
Original file line number Diff line number Diff line change
Expand Up @@ -3721,6 +3721,57 @@ void test_parseDNSAnswer_recordstored_gt_count2( void )
TEST_ASSERT_EQUAL( 80, uxBytesRead );
}

/**
* @brief Verify that parseDNSAnswer bounds answer parsing to
* ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY even when xDoStore is pdFALSE
* for an unsolicited reply.
*
* This ensures an attacker-controlled ANCOUNT does not cause the parser
* to iterate over an excessive number of advertised answers.
*/
void test_parseDNSAnswer_unsolicited_reply_loop_bounded( void )
{
BaseType_t ret;
DNSMessage_t pxDNSMessageHeader;
char pucByte[ 300 ];
size_t uxBytesRead = 0;
ParseSet_t xSet = { 0 };
struct freertos_addrinfo * pxAddressInfo = NULL;
int index = 0;
uint16_t i;

memset( pucByte, 0x00, sizeof( pucByte ) );

/* Place a valid name field for each expected iteration so that
* DNS_SkipNameField succeeds and usChar2u16 is reached every time. */
for( i = 0U; i < ( uint16_t ) ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY; i++ )
{
pucByte[ index ] = 38;
strcpy( pucByte + index + 1, "FreeRTOSbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" );
index += 40;
}

xSet.pxDNSMessageHeader = &pxDNSMessageHeader;
xSet.pucByte = pucByte;
xSet.uxSourceBytesRemaining = sizeof( pucByte );
xSet.xDoStore = pdFALSE;
xSet.usNumARecordsStored = 0;
xSet.usAnswers = 0xFFFFU; /* Simulated attacker-controlled ANCOUNT. */

/* Expect exactly ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY calls to
* usChar2u16. If the loop exceeds the cap, CMock fails on an
* unexpected call; if it falls short, CMock fails on an unmet
* expectation. */
for( i = 0U; i < ( uint16_t ) ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY; i++ )
{
usChar2u16_ExpectAnyArgsAndReturn( dnsTYPE_A_HOST );
}

ret = parseDNSAnswer( &xSet, &pxAddressInfo, &uxBytesRead );

TEST_ASSERT_EQUAL( 0, ret );
}

/**
* @brief ensures that when the number of entries is larger than the configured
* cache address entries, not packet is sent over the network
Expand Down
Loading