Skip to content
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

Fixing other potential memory corruption #155

Merged
merged 37 commits into from
Apr 13, 2018
Merged

Fixing other potential memory corruption #155

merged 37 commits into from
Apr 13, 2018

Conversation

huguesBouvier
Copy link
Contributor

Fixing other potential memory corruption bugs.
Also added a function to free up mutexes created by mqtt init and shadow init

Passed unit tests and integration tests

@@ -23,7 +23,7 @@

// Get from console
// =================================================
#define AWS_IOT_MQTT_HOST "" ///< Customer specific MQTT HOST. The same will be used for Thing Shadow
#define AWS_IOT_MQTT_HOST "a3qggb6vsdlf5s.iot.us-west-2.amazonaws.com" ///< Customer specific MQTT HOST. The same will be used for Thing Shadow
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Endpoint should be removed. Changes to this file shouldn't be committed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, fixed

rc = aws_iot_thread_mutex_destroy(&(pClient->clientData.state_change_mutex));
}

if ((&(pClient->clientData.state_change_mutex) != NULL)&&(rc == SUCCESS))
Copy link
Contributor

@gordonwang0 gordonwang0 Apr 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should check if tls_read_mutex isn't NULL.

How about also setting pClient->clientData.tls_read_mutex to NULL after destroying it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be a bug, it is a structure.

rc = aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_read_mutex));
}

if ((&(pClient->clientData.state_change_mutex) != NULL)&&(rc == SUCCESS))
Copy link
Contributor

@gordonwang0 gordonwang0 Apr 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should check if tls_write_mutex isn't NULL.

How about also setting pClient->clientData.tls_write_mutex to NULL after destroying it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be a bug, it is a structure.

#ifdef _ENABLE_THREAD_SUPPORT_
if ((&(pClient->clientData.state_change_mutex) != NULL)&&(rc == SUCCESS))
{
rc = aws_iot_thread_mutex_destroy(&(pClient->clientData.state_change_mutex));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about also setting pClient->clientData.state_change_mutex to NULL after destroying it?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure about handling in this way.
(&(pClient->clientData.state_change_mutex) != NULL) checks if the address of variable pClient->clientData.state_change_mutex is not NULL.
Then this address is passed to aws_iot_thread_mutex_destroy() by value; the function can free the content, but cannot modify the parameter passed by value (that is the address).
Even setting pClient->clientData.state_change_mutex to NULL (which I think it's a good idea), its address is not NULL.
And so calling aws_iot_mqtt_free() one more time the condition (&(pClient->clientData.state_change_mutex) != NULL) is true and so aws_iot_thread_mutex_destroy() would be called ahain.

Is this the expected beahvior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it is a bug, checking NULL is pointless, it is a structure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems there is not need to check, destroy will set it to an invalid value so subsequent call will return EINVALID

@@ -424,13 +462,13 @@ bool isReceivedJsonValid(const char *pJsonDocument) {
return true;
}

bool extractClientToken(const char *pJsonDocument, char *pExtractedClientToken) {
bool extractClientToken(const char *pJsonDocument, size_t jsonSize, char *pExtractedClientToken, size_t clientTokenSize) {
int32_t tokenCount, i;
uint8_t length;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure uint8_t (up to 255) is always sufficient? How about size_t instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still uint8_t in the latest version?

ret_val = isReceivedJsonValid(getRequestJson, TEST_JSON_SIZE);
CHECK_EQUAL_C_INT(true, ret_val);

IOT_DEBUG("-->Success - sReceivedJsonValid");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong test name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

bool ret_val;
char getRequestJson[TEST_JSON_SIZE];

IOT_DEBUG("-->Running Shadow Action Tests - sReceivedJsonValid \n");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong test name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still sReceivedJsonValid in the latest version?

ret_val = extractClientToken(getRequestJson, TEST_JSON_SIZE, extractedClientToken, MAX_SIZE_CLIENT_ID_WITH_SEQUENCE );
CHECK_EQUAL_C_INT(true, ret_val);

IOT_DEBUG("-->Success - getAndDeleteRequest");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong test name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@gordonwang0
Copy link
Contributor

Can you also change aws_iot_mqtt_client.c?

#ifdef _ENABLE_THREAD_SUPPORT_
pClient->clientData.isBlockOnThreadLockEnabled = pInitParams->isBlockOnThreadLockEnabled;
rc = aws_iot_thread_mutex_init(&(pClient->clientData.state_change_mutex));
if(SUCCESS != rc) {
FUNC_EXIT_RC(rc);
}
rc = aws_iot_thread_mutex_init(&(pClient->clientData.tls_read_mutex));
if(SUCCESS != rc) {
FUNC_EXIT_RC(rc);
}
rc = aws_iot_thread_mutex_init(&(pClient->clientData.tls_write_mutex));
if(SUCCESS != rc) {
FUNC_EXIT_RC(rc);
}
#endif

In aws_iot_mqtt_init, the mutexes state_change_mutex and tls_read_mutex and tls_write_mutex need to be destroyed before returning if there's an error after their creation to avoid a memory leak.

@huguesBouvier
Copy link
Contributor Author

Ok, I will change that.

rc = aws_iot_thread_mutex_destroy(&(pClient->clientData.state_change_mutex));
}

if (rc == SUCCESS)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should still attempt to destroy mutexes, even if the previous call to aws_iot_thread_mutex_destroy failed. We could also print a debug message if a call to aws_iot_thread_mutex_destroy fails.

FUNC_EXIT_RC(rc);
}
rc = aws_iot_thread_mutex_init(&(pClient->clientData.tls_write_mutex));
if(SUCCESS != rc) {
(void)aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_read_mutex));
(void)aws_iot_thread_mutex_destroy(&(pClient->clientData.state_change_mutex));
FUNC_EXIT_RC(rc);
}
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 263 also contains an exit on failure, should also destroy mutexes there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I agree totally with that process. The mqtt free should be called after init anyway. I think we are bringing complexity when not needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if mqtt_init returns something other than SUCCESS, users may not know they need to call mqtt_free. I think it makes sense for init to clean everything up if it fails.

Deleting as much mutexes as possible
adding destroy at the end of function
{
rc = aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_write_mutex));
}else{
(void)aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_read_mutex));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line should destroy tls_write_mutex.

Also, is it necessary to re-assign rc? Seems we could just do the following to clean everything up.

if(rc == SUCCESS) /* Check not NULL_VALUE_ERROR. */
{
    (void)aws_iot_thread_mutex_destroy(&(pClient->clientData.state_change_mutex));
    (void)aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_read_mutex));
    (void)aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_write_mutex));
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I though of that but it will not work unless you refactor the function because it has exit points everywhere

FUNC_EXIT_RC(rc);
}
rc = aws_iot_thread_mutex_init(&(pClient->clientData.tls_write_mutex));
if(SUCCESS != rc) {
(void)aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_read_mutex));
(void)aws_iot_thread_mutex_destroy(&(pClient->clientData.state_change_mutex));
FUNC_EXIT_RC(rc);
}
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if mqtt_init returns something other than SUCCESS, users may not know they need to call mqtt_free. I think it makes sense for init to clean everything up if it fails.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants