Skip to content

Commit 6475c9f

Browse files
authored
Merge pull request #357 from Azure/ewertons/fixlinkepdoubledestroy
Fix potential double free of link endpoint by link.c
2 parents b0e00a6 + fea2f58 commit 6475c9f

File tree

3 files changed

+48
-0
lines changed

3 files changed

+48
-0
lines changed

inc/azure_uamqp_c/session.h

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ MU_DEFINE_ENUM(SESSION_STATE, SESSION_STATE_VALUES)
5858
MU_DEFINE_ENUM(SESSION_SEND_TRANSFER_RESULT, SESSION_SEND_TRANSFER_RESULT_VALUES)
5959

6060
typedef void(*LINK_ENDPOINT_FRAME_RECEIVED_CALLBACK)(void* context, AMQP_VALUE performative, uint32_t frame_payload_size, const unsigned char* payload_bytes);
61+
typedef void(*ON_LINK_ENDPOINT_DESTROYED_CALLBACK)(LINK_ENDPOINT_HANDLE handle, void* context);
6162
typedef void(*ON_SESSION_STATE_CHANGED)(void* context, SESSION_STATE new_session_state, SESSION_STATE previous_session_state);
6263
typedef void(*ON_SESSION_FLOW_ON)(void* context);
6364
typedef bool(*ON_LINK_ATTACHED)(void* context, LINK_ENDPOINT_HANDLE new_link_endpoint, const char* name, role role, AMQP_VALUE source, AMQP_VALUE target, fields properties);
@@ -74,6 +75,7 @@ MU_DEFINE_ENUM(SESSION_SEND_TRANSFER_RESULT, SESSION_SEND_TRANSFER_RESULT_VALUES
7475
MOCKABLE_FUNCTION(, int, session_begin, SESSION_HANDLE, session);
7576
MOCKABLE_FUNCTION(, int, session_end, SESSION_HANDLE, session, const char*, condition_value, const char*, description);
7677
MOCKABLE_FUNCTION(, LINK_ENDPOINT_HANDLE, session_create_link_endpoint, SESSION_HANDLE, session, const char*, name);
78+
MOCKABLE_FUNCTION(, void, session_set_link_endpoint_callback, LINK_ENDPOINT_HANDLE, link_endpoint, ON_LINK_ENDPOINT_DESTROYED_CALLBACK, on_link_endpoint_destroyed, void*, context);
7779
MOCKABLE_FUNCTION(, void, session_destroy_link_endpoint, LINK_ENDPOINT_HANDLE, link_endpoint);
7880
MOCKABLE_FUNCTION(, int, session_start_link_endpoint, LINK_ENDPOINT_HANDLE, link_endpoint, ON_ENDPOINT_FRAME_RECEIVED, frame_received_callback, ON_SESSION_STATE_CHANGED, on_session_state_changed, ON_SESSION_FLOW_ON, on_session_flow_on, void*, context);
7981
MOCKABLE_FUNCTION(, int, session_send_flow, LINK_ENDPOINT_HANDLE, link_endpoint, FLOW_HANDLE, flow);

src/link.c

+23
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,20 @@ static void on_send_complete(void* context, IO_SEND_RESULT send_result)
701701
}
702702
}
703703

704+
static void on_link_endpoint_destroyed(LINK_ENDPOINT_HANDLE handle, void* context)
705+
{
706+
if (context != NULL)
707+
{
708+
LINK_INSTANCE* link = (LINK_INSTANCE*)context;
709+
710+
if (link->link_endpoint == handle)
711+
{
712+
// In this case the link endpoint has been destroyed by the session, and link should not try to free it.
713+
link->link_endpoint = NULL;
714+
}
715+
}
716+
}
717+
704718
LINK_HANDLE link_create(SESSION_HANDLE session, const char* name, role role, AMQP_VALUE source, AMQP_VALUE target)
705719
{
706720
LINK_INSTANCE* result = (LINK_INSTANCE*)calloc(1, sizeof(LINK_INSTANCE));
@@ -779,6 +793,12 @@ LINK_HANDLE link_create(SESSION_HANDLE session, const char* name, role role, AMQ
779793
free(result);
780794
result = NULL;
781795
}
796+
else
797+
{
798+
// This ensures link.c gets notified if the link endpoint is destroyed
799+
// by uamqp (due to a DETACH from the hub, e.g.) to prevent a double free.
800+
session_set_link_endpoint_callback(result->link_endpoint, on_link_endpoint_destroyed, result);
801+
}
782802
}
783803
}
784804
}
@@ -883,6 +903,9 @@ void link_destroy(LINK_HANDLE link)
883903

884904
link->on_link_state_changed = NULL;
885905
(void)link_detach(link, true, NULL, NULL, NULL);
906+
// This is required to suppress the link destroyed callback, since this function is
907+
// actively requesting that (in the following line).
908+
session_set_link_endpoint_callback(link->link_endpoint, NULL, NULL);
886909
session_destroy_link_endpoint(link->link_endpoint);
887910
amqpvalue_destroy(link->source);
888911
amqpvalue_destroy(link->target);

src/session.c

+23
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ typedef struct LINK_ENDPOINT_INSTANCE_TAG
2727
void* callback_context;
2828
SESSION_HANDLE session;
2929
LINK_ENDPOINT_STATE link_endpoint_state;
30+
ON_LINK_ENDPOINT_DESTROYED_CALLBACK on_link_endpoint_destroyed_callback;
31+
void* on_link_endpoint_destroyed_context;
3032
} LINK_ENDPOINT_INSTANCE;
3133

3234
typedef struct SESSION_INSTANCE_TAG
@@ -104,6 +106,16 @@ static void remove_link_endpoint(LINK_ENDPOINT_HANDLE link_endpoint)
104106

105107
static void free_link_endpoint(LINK_ENDPOINT_HANDLE link_endpoint)
106108
{
109+
// The link endpoint handle can be managed by both uamqp and the upper layer.
110+
// uamqp may destroy a link endpoint if a DETACH is received from the remote endpoint,
111+
// so in this case the upper layer must be notified so it does not attempt to destroy the link endpoint as well.
112+
// Ref counting would not suffice to address this situation as uamqp does not destroy link endpoints by itself
113+
// every time, only when receiving a DETACH.
114+
if (link_endpoint->on_link_endpoint_destroyed_callback != NULL)
115+
{
116+
link_endpoint->on_link_endpoint_destroyed_callback(link_endpoint, link_endpoint->on_link_endpoint_destroyed_context);
117+
}
118+
107119
if (link_endpoint->name != NULL)
108120
{
109121
free(link_endpoint->name);
@@ -1121,6 +1133,8 @@ LINK_ENDPOINT_HANDLE session_create_link_endpoint(SESSION_HANDLE session, const
11211133
result->link_endpoint_state = LINK_ENDPOINT_STATE_NOT_ATTACHED;
11221134
name_length = strlen(name);
11231135
result->name = (char*)malloc(name_length + 1);
1136+
result->on_link_endpoint_destroyed_callback = NULL;
1137+
result->on_link_endpoint_destroyed_context = NULL;
11241138
if (result->name == NULL)
11251139
{
11261140
/* Codes_S_R_S_SESSION_01_045: [If allocating memory for the link endpoint fails, session_create_link_endpoint shall fail and return NULL.] */
@@ -1160,6 +1174,15 @@ LINK_ENDPOINT_HANDLE session_create_link_endpoint(SESSION_HANDLE session, const
11601174
return result;
11611175
}
11621176

1177+
void session_set_link_endpoint_callback(LINK_ENDPOINT_HANDLE link_endpoint, ON_LINK_ENDPOINT_DESTROYED_CALLBACK on_link_endpoint_destroyed, void* context)
1178+
{
1179+
if (link_endpoint != NULL)
1180+
{
1181+
link_endpoint->on_link_endpoint_destroyed_callback = on_link_endpoint_destroyed;
1182+
link_endpoint->on_link_endpoint_destroyed_context = context;
1183+
}
1184+
}
1185+
11631186
void session_destroy_link_endpoint(LINK_ENDPOINT_HANDLE link_endpoint)
11641187
{
11651188
if (link_endpoint != NULL)

0 commit comments

Comments
 (0)