Add guard against crash in amqp messenger/mq (#2608)

There are instances where a message will be destroyed at the iothubtransport amqp messeger layer,
but be still in flight at the messagesender (uamqp) layer, running at the risk of messagesender
invoking the send complete callback and resulting in a memory read error over an address previously
freed. Using the async operation feature of uamqp adds an extra layer of safety to cancel the
existing send operation, preventing the callback to be fired improperly.
This commit is contained in:
Ewerton Scaboro da Silva 2024-04-11 15:35:55 -07:00 коммит произвёл GitHub
Родитель 15171e9fa7
Коммит fce8040dfa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
4 изменённых файлов: 26 добавлений и 21 удалений

Просмотреть файл

@ -303,7 +303,7 @@ int main(void)
ThreadAPI_Sleep((unsigned int)sleep_ms);
counter += sleep_ms;
g_continueRunning = !(messages_sent > MESSAGE_COUNT);
g_continueRunning &= !(messages_sent > MESSAGE_COUNT);
} while (g_continueRunning);
(void)printf("client_amqp_shared_sample has received a quit message, call DoWork %d more time to complete final sending...\r\n", DOWORK_LOOP_NUM);

Просмотреть файл

@ -14,6 +14,7 @@
#include "azure_uamqp_c/messaging.h"
#include "azure_uamqp_c/message_sender.h"
#include "azure_uamqp_c/message_receiver.h"
#include "azure_uamqp_c/async_operation.h"
#include "internal/message_queue.h"
#include "internal/iothub_client_retry_control.h"
#include "internal/iothubtransport_amqp_messenger.h"
@ -86,9 +87,24 @@ typedef struct MESSAGE_SEND_CONTEXT_TAG
void* user_context;
PROCESS_MESSAGE_COMPLETED_CALLBACK on_process_message_completed_callback;
// Handle to the async operation instance returned
// by messagesender_send_async().
ASYNC_OPERATION_HANDLE async_operation;
} MESSAGE_SEND_CONTEXT;
static void destroy_message_send_context(MESSAGE_SEND_CONTEXT* context)
{
if (context->async_operation != NULL)
{
(void)async_operation_cancel(context->async_operation);
}
free(context);
}
#define mark_message_send_context_completed(context) \
context->async_operation = NULL
static MESSAGE_SEND_CONTEXT* create_message_send_context(void)
{
@ -272,11 +288,6 @@ static AMQP_MESSENGER_CONFIG* clone_configuration(const AMQP_MESSENGER_CONFIG* c
return result;
}
static void destroy_message_send_context(MESSAGE_SEND_CONTEXT* context)
{
free(context);
}
static STRING_HANDLE create_link_address(const char* host_fqdn, const char* device_id, const char* module_id, const char* address_suffix)
{
STRING_HANDLE link_address;
@ -850,6 +861,8 @@ static void on_send_complete_callback(void* context, MESSAGE_SEND_RESULT send_re
MESSAGE_QUEUE_RESULT mq_result;
MESSAGE_SEND_CONTEXT* msg_ctx = (MESSAGE_SEND_CONTEXT*)context;
mark_message_send_context_completed(msg_ctx);
if (send_result == MESSAGE_SEND_OK)
{
mq_result = MESSAGE_QUEUE_SUCCESS;
@ -874,8 +887,9 @@ static void on_process_message_callback(MESSAGE_QUEUE_HANDLE message_queue, MQ_M
MESSAGE_SEND_CONTEXT* message_context = (MESSAGE_SEND_CONTEXT*)context;
message_context->mq_message_id = message_id;
message_context->on_process_message_completed_callback = on_process_message_completed_callback;
message_context->async_operation = messagesender_send_async(message_context->messenger->message_sender, (MESSAGE_HANDLE)message, on_send_complete_callback, context, 0);
if (messagesender_send_async(message_context->messenger->message_sender, (MESSAGE_HANDLE)message, on_send_complete_callback, context, 0) == NULL)
if (message_context->async_operation == NULL)
{
LogError("Failed sending AMQP message");
on_process_message_completed_callback(message_queue, message_id, MESSAGE_QUEUE_ERROR, NULL);

Просмотреть файл

@ -258,10 +258,7 @@ static void process_pending_messages(MESSAGE_QUEUE_HANDLE message_queue)
{
LogError("failed moving message out of pending list (%p)", mq_item->message);
if (mq_item->on_message_processing_completed_callback != NULL)
{
mq_item->on_message_processing_completed_callback(mq_item->message, MESSAGE_QUEUE_ERROR, NULL, mq_item->user_context);
}
fire_message_callback(mq_item, MESSAGE_QUEUE_ERROR, NULL);
// Not freeing since this would cause a memory A/V on the next call.
@ -271,22 +268,14 @@ static void process_pending_messages(MESSAGE_QUEUE_HANDLE message_queue)
{
LogError("failed setting message processing_start_time (%p)", mq_item->message);
if (mq_item->on_message_processing_completed_callback != NULL)
{
mq_item->on_message_processing_completed_callback(mq_item->message, MESSAGE_QUEUE_ERROR, NULL, mq_item->user_context);
}
fire_message_callback(mq_item, MESSAGE_QUEUE_ERROR, NULL);
free(mq_item);
}
else if (singlylinkedlist_add(message_queue->in_progress, (const void*)mq_item) == NULL)
{
LogError("failed moving message to in-progress list (%p)", mq_item->message);
if (mq_item->on_message_processing_completed_callback != NULL)
{
mq_item->on_message_processing_completed_callback(mq_item->message, MESSAGE_QUEUE_ERROR, NULL, mq_item->user_context);
}
fire_message_callback(mq_item, MESSAGE_QUEUE_ERROR, NULL);
free(mq_item);
}
else

Просмотреть файл

@ -89,12 +89,14 @@ static void real_free(void* ptr)
#define please_mock_messaging_delivery_accepted MOCK_ENABLED
#define please_mock_messaging_delivery_rejected MOCK_ENABLED
#define please_mock_messaging_delivery_released MOCK_ENABLED
#define please_mock_async_operation_cancel MOCK_ENABLED
#include "azure_uamqp_c/session.h"
#include "azure_uamqp_c/link.h"
#include "azure_uamqp_c/messaging.h"
#include "azure_uamqp_c/message_sender.h"
#include "azure_uamqp_c/message_receiver.h"
#include "azure_uamqp_c/async_operation.h"
#include "internal/message_queue.h"
#undef ENABLE_MOCK_FILTERING_SWITCH