From b04c65f3c1a6ab3b81e4e51e319c3ec9ad44d703 Mon Sep 17 00:00:00 2001
From: Aniruddha Kanhere <60444055+AniruddhaKanhere@users.noreply.github.com>
Date: Wed, 21 Sep 2022 15:26:58 -0700
Subject: [PATCH] Fix bugs in receiveSingleIteration and optimize
sendMessageVector (#218)
* Update the timout check in the send function
* Fix direction of check
* Allow processing of data in the buffer
* Fix formatting and unit-tests
* Update comment to clarify control flow
* Fix Disconnect CBMC proof and update size table
* Fix formatting and CBMC proofs
* Fix last CBMC proof
* Fix broken unit tests and add branch coverage
---
docs/doxygen/include/size_table.md | 4 +-
lexicon.txt | 1 +
source/core_mqtt.c | 35 +++++++--
source/core_mqtt_serializer.c | 3 +
.../MQTT_Disconnect/MQTT_Disconnect_harness.c | 2 +
test/unit-test/core_mqtt_utest.c | 78 ++++++++++++++++++-
6 files changed, 111 insertions(+), 12 deletions(-)
diff --git a/docs/doxygen/include/size_table.md b/docs/doxygen/include/size_table.md
index 8aeb5ec40..4d198e3ab 100644
--- a/docs/doxygen/include/size_table.md
+++ b/docs/doxygen/include/size_table.md
@@ -10,7 +10,7 @@
core_mqtt.c |
3.9K |
- 3.3K |
+ 3.4K |
core_mqtt_state.c |
@@ -25,6 +25,6 @@
Total estimates |
8.4K |
- 6.8K |
+ 6.9K |
diff --git a/lexicon.txt b/lexicon.txt
index e27c0df01..bf60d72ec 100644
--- a/lexicon.txt
+++ b/lexicon.txt
@@ -17,6 +17,7 @@ bytesreceived
bytesrecvd
bytesremaining
bytessent
+bytessentthisvector
bytestoread
bytestoreceive
bytestorecv
diff --git a/source/core_mqtt.c b/source/core_mqtt.c
index bd51beebd..22c361c02 100644
--- a/source/core_mqtt.c
+++ b/source/core_mqtt.c
@@ -736,7 +736,6 @@ static int32_t sendMessageVector( MQTTContext_t * pContext,
uint32_t timeoutTime;
uint32_t bytesToSend = 0U;
int32_t bytesSentOrError = 0;
- uint64_t temp = 0;
TransportOutVector_t * pIoVectIterator;
size_t vectorsToBeSent = ioVecCount;
@@ -751,15 +750,13 @@ static int32_t sendMessageVector( MQTTContext_t * pContext,
/* Count the total number of bytes to be sent as outlined in the vector. */
for( pIoVectIterator = pIoVec; pIoVectIterator <= &( pIoVec[ ioVecCount - 1U ] ); pIoVectIterator++ )
{
- temp += pIoVectIterator->iov_len;
bytesToSend += ( uint32_t ) pIoVectIterator->iov_len;
}
/* Reset the iterator to point to the first entry in the array. */
pIoVectIterator = pIoVec;
- while( ( pContext->getTime() < timeoutTime ) &&
- ( bytesSentOrError < ( int32_t ) bytesToSend ) )
+ while( bytesSentOrError < ( int32_t ) bytesToSend )
{
int32_t sendResult;
uint32_t bytesSentThisVector = 0U;
@@ -785,6 +782,7 @@ static int32_t sendMessageVector( MQTTContext_t * pContext,
else
{
bytesSentOrError = sendResult;
+
break;
}
@@ -806,6 +804,12 @@ static int32_t sendMessageVector( MQTTContext_t * pContext,
pIoVectIterator->iov_base = ( const void * ) &( ( ( const uint8_t * ) pIoVectIterator->iov_base )[ bytesSentThisVector ] );
pIoVectIterator->iov_len -= bytesSentThisVector;
}
+
+ /* Check for timeout. */
+ if( pContext->getTime() > timeoutTime )
+ {
+ break;
+ }
}
return bytesSentOrError;
@@ -816,7 +820,7 @@ static int32_t sendBuffer( MQTTContext_t * pContext,
size_t bytesToSend )
{
const uint8_t * pIndex = pBufferToSend;
- size_t bytesRemaining = bytesToSend;
+ size_t bytesRemaining;
int32_t totalBytesSent = 0, bytesSent;
uint32_t lastSendTimeMs = 0U, timeSinceLastSendMs = 0U;
bool sendError = false;
@@ -1650,11 +1654,15 @@ static MQTTStatus_t receiveSingleIteration( MQTTContext_t * pContext,
/* The receive function has failed. Bubble up the error up to the user. */
status = MQTTRecvFailed;
}
- else if( recvBytes == 0 )
+ else if( ( recvBytes == 0 ) && ( pContext->index == 0 ) )
{
- /* No more bytes available since the last read. */
+ /* No more bytes available since the last read and neither is anything in
+ * the buffer. */
status = MQTTNoDataAvailable;
}
+
+ /* Either something was received, or there is still data to be processed in the
+ * buffer, or both. */
else
{
/* Update the number of bytes in the MQTT fixed buffer. */
@@ -2088,6 +2096,11 @@ static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext,
LogError( ( "pWillInfo->pTopicName cannot be NULL if Will is present." ) );
status = MQTTBadParameter;
}
+ else if( ( pWillInfo != NULL ) && ( pWillInfo->pPayload == NULL ) )
+ {
+ LogError( ( "pWillInfo->pPayload cannot be NULL if Will is present." ) );
+ status = MQTTBadParameter;
+ }
else
{
pIndex = MQTT_SerializeConnectFixedHeader( pIndex,
@@ -2321,6 +2334,10 @@ static MQTTStatus_t handleSessionResumption( MQTTContext_t * pContext,
assert( pContext != NULL );
+ /* Reset the index and clear the buffer when a new session is established. */
+ pContext->index = 0;
+ memset( pContext->networkBuffer.pBuffer, 0, pContext->networkBuffer.size );
+
if( sessionPresent == true )
{
/* Get the next packet ID for which a PUBREL need to be resent. */
@@ -2969,6 +2986,10 @@ MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext )
{
LogInfo( ( "Disconnected from the broker." ) );
pContext->connectStatus = MQTTNotConnected;
+
+ /* Reset the index and clean the buffer on a successful disconnect. */
+ pContext->index = 0;
+ ( void ) memset( pContext->networkBuffer.pBuffer, 0, pContext->networkBuffer.size );
}
return status;
diff --git a/source/core_mqtt_serializer.c b/source/core_mqtt_serializer.c
index 595ecd2fc..57b63eaa7 100644
--- a/source/core_mqtt_serializer.c
+++ b/source/core_mqtt_serializer.c
@@ -868,6 +868,8 @@ static MQTTStatus_t processRemainingLength( const uint8_t * pBuffer,
{
remainingLength = MQTT_REMAINING_LENGTH_INVALID;
+ LogError( ( "Invalid remaining length in the packet.\n" ) );
+
status = MQTTBadResponse;
}
else
@@ -904,6 +906,7 @@ static MQTTStatus_t processRemainingLength( const uint8_t * pBuffer,
if( bytesDecoded != expectedSize )
{
+ LogError( ( "Expected and actual length of decoded bytes do not match.\n" ) );
status = MQTTBadResponse;
}
else
diff --git a/test/cbmc/proofs/MQTT_Disconnect/MQTT_Disconnect_harness.c b/test/cbmc/proofs/MQTT_Disconnect/MQTT_Disconnect_harness.c
index 7a751a16a..ea6397289 100644
--- a/test/cbmc/proofs/MQTT_Disconnect/MQTT_Disconnect_harness.c
+++ b/test/cbmc/proofs/MQTT_Disconnect/MQTT_Disconnect_harness.c
@@ -33,6 +33,8 @@ void harness()
pContext = allocateMqttContext( NULL );
__CPROVER_assume( isValidMqttContext( pContext ) );
+ __CPROVER_assume( pContext != NULL );
+ __CPROVER_assume( pContext->networkBuffer.pBuffer != NULL );
MQTT_Disconnect( pContext );
}
diff --git a/test/unit-test/core_mqtt_utest.c b/test/unit-test/core_mqtt_utest.c
index 094ad349b..afe3312c6 100644
--- a/test/unit-test/core_mqtt_utest.c
+++ b/test/unit-test/core_mqtt_utest.c
@@ -1025,6 +1025,8 @@ void test_MQTT_Connect_ProperWIllInfo( void )
willInfo.pTopicName = "MQTTTopic";
willInfo.topicNameLength = strlen( willInfo.pTopicName );
+ willInfo.pPayload = "MQTTPayload";
+ willInfo.payloadLength = strlen( willInfo.pPayload );
connectInfo.pUserName = "MQTTUser";
connectInfo.userNameLength = strlen( connectInfo.pUserName );
@@ -1054,6 +1056,60 @@ void test_MQTT_Connect_ProperWIllInfo( void )
TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status );
}
+/**
+ * @brief Test MQTT_Connect, with no payload in will message.
+ */
+void test_MQTT_Connect_ImproperWIllInfo( void )
+{
+ MQTTContext_t mqttContext = { 0 };
+ MQTTConnectInfo_t connectInfo = { 0 };
+ uint32_t timeout = 2;
+ bool sessionPresent;
+ MQTTStatus_t status;
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ size_t remainingLength;
+ size_t packetSize;
+ MQTTPublishInfo_t willInfo;
+
+ setupTransportInterface( &transport );
+ transport.writev = transportWritevFail;
+ setupNetworkBuffer( &networkBuffer );
+
+ memset( &mqttContext, 0x0, sizeof( mqttContext ) );
+ memset( &willInfo, 0, sizeof( MQTTPublishInfo_t ) );
+
+ willInfo.pTopicName = "MQTTTopic";
+ willInfo.topicNameLength = strlen( willInfo.pTopicName );
+
+ connectInfo.pUserName = "MQTTUser";
+ connectInfo.userNameLength = strlen( connectInfo.pUserName );
+ connectInfo.pPassword = "NotSafePassword";
+ connectInfo.passwordLength = strlen( connectInfo.pPassword );
+
+ MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
+
+ /* Transport send failed when sending CONNECT. */
+
+ /* Choose 10 bytes variable header + 1 byte payload for the remaining
+ * length of the CONNECT. The packet size needs to be nonzero for this test
+ * as that is the amount of bytes used in the call to send the packet. */
+ packetSize = 13;
+ remainingLength = 11;
+ mqttContext.transportInterface.send = transportSendFailure;
+ MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb );
+ MQTT_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_GetConnectPacketSize_IgnoreArg_pPacketSize();
+ MQTT_GetConnectPacketSize_IgnoreArg_pRemainingLength();
+ MQTT_GetConnectPacketSize_ReturnThruPtr_pPacketSize( &packetSize );
+ MQTT_GetConnectPacketSize_ReturnThruPtr_pRemainingLength( &remainingLength );
+ MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess );
+
+ status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent );
+
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+}
+
/**
* @brief Test MQTT_Connect, except for receiving the CONNACK.
*/
@@ -1633,6 +1689,8 @@ void test_MQTT_Connect_happy_path3()
willInfo.pTopicName = "test";
willInfo.topicNameLength = 4;
+ willInfo.pPayload = "Payload";
+ willInfo.payloadLength = 7;
incomingPacket.type = MQTT_PACKET_TYPE_CONNACK;
MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess );
MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess );
@@ -2545,6 +2603,9 @@ void test_MQTT_Disconnect4( void )
MQTTFixedBuffer_t networkBuffer = { 0 };
size_t disconnectSize = 2;
+ /* Fill the buffer with garbage data. */
+ memset( mqttBuffer, 0xAB, MQTT_TEST_BUFFER_LENGTH );
+
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
networkContext.buffer = &bufPtr;
@@ -2570,7 +2631,8 @@ void test_MQTT_Disconnect4( void )
TEST_ASSERT_EQUAL( MQTTSuccess, status );
TEST_ASSERT_EQUAL( MQTTNotConnected, mqttContext.connectStatus );
- TEST_ASSERT_EQUAL_MEMORY( mqttBuffer, buffer, 1 );
+ /* At disconnect, the buffer is cleared of any pending packets. */
+ TEST_ASSERT_EACH_EQUAL_UINT8( 0, mqttBuffer, MQTT_TEST_BUFFER_LENGTH );
}
/* ========================================================================== */
@@ -3886,11 +3948,21 @@ void test_MQTT_ReceiveLoop( void )
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
setupNetworkBuffer( &( context.networkBuffer ) );
- MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTRecvFailed );
+ MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTBadResponse );
/* Error case, for branch coverage. */
mqttStatus = MQTT_ReceiveLoop( &context );
- TEST_ASSERT_EQUAL( MQTTRecvFailed, mqttStatus );
+ TEST_ASSERT_EQUAL( MQTTBadResponse, mqttStatus );
+
+ /* This will cover the case when there is data available in the buffer to be processed but
+ * no additional bytes have been received by the transport interface. */
+ context.transportInterface.recv = transportRecvNoData;
+ MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTBadResponse );
+ /* Error case, for branch coverage. */
+ mqttStatus = MQTT_ReceiveLoop( &context );
+ TEST_ASSERT_EQUAL( MQTTBadResponse, mqttStatus );
+ /* Reset the index to clear the buffer of any remaining data. */
+ context.index = 0;
/* Keep Alive should not trigger.*/
context.keepAliveIntervalSec = 1;
mqttStatus = MQTT_ReceiveLoop( &context );