Skip to content

Commit 0ac5158

Browse files
committed
Fixes skupperproject#1432: prevent use-after-free race in cutthrough activation.
The patch adds locking to the cutthrough activation logic that prevents the activation handler from being released while another thread attempts using the activation. Closes skupperproject#1432
1 parent 12e7e2b commit 0ac5158

File tree

9 files changed

+191
-174
lines changed

9 files changed

+191
-174
lines changed

include/qpid/dispatch/cutthrough_utils.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
*
2828
* @param msg - Pointer to a stream
2929
*/
30-
void cutthrough_notify_buffers_produced_inbound(qd_message_t *msg);
31-
void cutthrough_notify_buffers_consumed_outbound(qd_message_t *msg);
30+
void cutthrough_notify_buffers_produced_inbound(const qd_message_activation_t *activation);
31+
void cutthrough_notify_buffers_consumed_outbound(const qd_message_activation_t *activation);
3232

3333
#endif

include/qpid/dispatch/message.h

+6-22
Original file line numberDiff line numberDiff line change
@@ -754,20 +754,6 @@ void qd_message_produce_buffers(qd_message_t *stream, qd_buffer_list_t *buffers)
754754
int qd_message_consume_buffers(qd_message_t *stream, qd_buffer_list_t *buffers, int limit);
755755

756756

757-
/**
758-
* Indicate whether this stream should be resumed from a stalled state. This will be the case
759-
* if (a) the stream was stalled due to being full, and (b) the payload has shrunk down below
760-
* the resume threshold.
761-
*
762-
* If the result is true, there is a side effect of clearing the 'stalled' state.
763-
*
764-
* @param stream Pointer to the message
765-
* @return true Yes, the stream was stalled and buffer production may continue
766-
* @return false No, the stream was not stalled or it was stalled and is not yet ready to resume
767-
*/
768-
bool qd_message_resume_from_stalled(qd_message_t *stream);
769-
770-
771757
typedef enum {
772758
QD_ACTIVATION_NONE = 0,
773759
QD_ACTIVATION_AMQP,
@@ -784,33 +770,31 @@ typedef struct {
784770
* Tell the message stream which connection is consuming its buffers.
785771
*
786772
* @param stream Pointer to the message
787-
* @param connection Pointer to the qd_connection that is consuming this stream's buffers
773+
* @param activation Parameters for activating the consuming I/O thread
788774
*/
789775
void qd_message_set_consumer_activation(qd_message_t *stream, qd_message_activation_t *activation);
790776

791777
/**
792-
* Return the connection that is consuming this message stream's buffers.
778+
* Cancel the activation. No further activations will be occur on return from this call.
793779
*
794780
* @param stream Pointer to the message
795-
* @return qd_connection_t* Pointer to the connection that is consuming buffers from this stream
796781
*/
797-
void qd_message_get_consumer_activation(const qd_message_t *stream, qd_message_activation_t *activation);
782+
void qd_message_cancel_consumer_activation(qd_message_t *stream);
798783

799784
/**
800785
* Tell the message stream which connection is producing its buffers.
801786
*
802787
* @param stream Pointer to the message
803-
* @param connection Pointer to the qd_connection that is consuming this stream's buffers
788+
* @param activation Parameters for activating the producing I/O thread
804789
*/
805790
void qd_message_set_producer_activation(qd_message_t *stream, qd_message_activation_t *activation);
806791

807792
/**
808-
* Return the connection that is producing this message stream's buffers.
793+
* Cancel the activation. No further activations will occur on return from this call.
809794
*
810795
* @param stream Pointer to the message
811-
* @return qd_connection_t* Pointer to the connection that is consuming buffers from this stream
812796
*/
813-
void qd_message_get_producer_activation(const qd_message_t *stream, qd_message_activation_t *activation);
797+
void qd_message_cancel_producer_activation(qd_message_t *stream);
814798

815799
///@}
816800

src/adaptors/tcp_lite/tcp_lite.c

+35-36
Original file line numberDiff line numberDiff line change
@@ -539,17 +539,14 @@ static void close_connection_XSIDE_IO(tcplite_connection_t *conn, bool no_delay)
539539

540540
free(conn->reply_to);
541541

542-
qd_message_activation_t activation;
543-
activation.type = QD_ACTIVATION_NONE;
544-
activation.delivery = 0;
545-
qd_nullify_safe_ptr(&activation.safeptr);
546-
547542
if (!!conn->inbound_stream) {
548-
qd_message_set_producer_activation(conn->inbound_stream, &activation);
543+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, DLV_FMT " TCP cancel producer activation", DLV_ARGS(conn->inbound_delivery));
544+
qd_message_cancel_producer_activation(conn->inbound_stream);
549545
}
550546

551547
if (!!conn->outbound_stream) {
552-
qd_message_set_consumer_activation(conn->outbound_stream, &activation);
548+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, DLV_FMT " TCP cancel consumer activation", DLV_ARGS(conn->outbound_delivery));
549+
qd_message_cancel_consumer_activation(conn->outbound_stream);
553550
}
554551

555552
if (!!conn->inbound_delivery) {
@@ -917,6 +914,17 @@ static void link_setup_CSIDE_IO(tcplite_connection_t *conn, qdr_delivery_t *deli
917914
qdr_link_set_context(conn->inbound_link, conn);
918915
conn->outbound_link = qdr_link_first_attach(conn->core_conn, QD_OUTGOING, qdr_terminus(0), qdr_terminus(0), "tcp.cside.out", 0, false, delivery, &conn->outbound_link_id);
919916
qdr_link_set_context(conn->outbound_link, conn);
917+
918+
// now that the raw connection is up and able to be activated enable cutthrough activation
919+
920+
assert(conn->outbound_stream);
921+
qd_message_activation_t activation;
922+
activation.type = QD_ACTIVATION_TCP;
923+
activation.delivery = 0;
924+
qd_alloc_set_safe_ptr(&activation.safeptr, conn);
925+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, DLV_FMT " TCP enabling consumer activation", DLV_ARGS(delivery));
926+
qd_message_set_consumer_activation(conn->outbound_stream, &activation);
927+
qd_message_start_unicast_cutthrough(conn->outbound_stream);
920928
}
921929

922930

@@ -983,6 +991,7 @@ static bool try_compose_and_send_client_stream_LSIDE_IO(tcplite_connection_t *co
983991
activation.type = QD_ACTIVATION_TCP;
984992
activation.delivery = 0;
985993
qd_alloc_set_safe_ptr(&activation.safeptr, conn);
994+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, "[C%" PRIu64 "][L%" PRIu64 "] TCP enabling producer activation", conn->conn_id, conn->inbound_link_id);
986995
qd_message_set_producer_activation(conn->inbound_stream, &activation);
987996
qd_message_start_unicast_cutthrough(conn->inbound_stream);
988997

@@ -1049,6 +1058,7 @@ static void compose_and_send_server_stream_CSIDE_IO(tcplite_connection_t *conn)
10491058
activation.type = QD_ACTIVATION_TCP;
10501059
activation.delivery = 0;
10511060
qd_alloc_set_safe_ptr(&activation.safeptr, conn);
1061+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, "[C%" PRIu64 "][L%" PRIu64 "] TCP enabling producer activation", conn->conn_id, conn->inbound_link_id);
10521062
qd_message_set_producer_activation(conn->inbound_stream, &activation);
10531063
qd_message_start_unicast_cutthrough(conn->inbound_stream);
10541064

@@ -1119,6 +1129,7 @@ static uint64_t handle_outbound_delivery_LSIDE_IO(tcplite_connection_t *conn, qd
11191129
activation.type = QD_ACTIVATION_TCP;
11201130
activation.delivery = 0;
11211131
qd_alloc_set_safe_ptr(&activation.safeptr, conn);
1132+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, DLV_FMT " TCP enabling consumer activation", DLV_ARGS(delivery));
11221133
qd_message_set_consumer_activation(conn->outbound_stream, &activation);
11231134
qd_message_start_unicast_cutthrough(conn->outbound_stream);
11241135
}
@@ -1184,16 +1195,6 @@ static uint64_t handle_first_outbound_delivery_CSIDE(tcplite_connector_t *cr, qd
11841195
conn->raw_conn = pn_raw_connection();
11851196
pn_raw_connection_set_context(conn->raw_conn, &conn->context);
11861197

1187-
//
1188-
// Message validation ensures the start of the message body is present. Activate cutthrough on the body data.
1189-
//
1190-
qd_message_activation_t activation;
1191-
activation.type = QD_ACTIVATION_TCP;
1192-
activation.delivery = 0;
1193-
qd_alloc_set_safe_ptr(&activation.safeptr, conn);
1194-
qd_message_set_consumer_activation(conn->outbound_stream, &activation);
1195-
qd_message_start_unicast_cutthrough(conn->outbound_stream);
1196-
11971198
//
11981199
// The raw connection establishment must be the last thing done in this function.
11991200
// After this call, a separate IO thread may immediately be invoked in the context
@@ -1274,12 +1275,16 @@ static bool manage_flow_XSIDE_IO(tcplite_connection_t *conn)
12741275
// If the raw connection is read-closed and the last produce did not block, settle and complete
12751276
// the inbound stream/delivery and close out the inbound half of the connection.
12761277
//
1278+
12771279
if (read_closed) {
1278-
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, DLV_FMT " Raw conn read-closed - close inbound delivery", DLV_ARGS(conn->inbound_delivery));
1280+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG,
1281+
DLV_FMT " Raw conn read-closed - close inbound delivery, cancel producer activation",
1282+
DLV_ARGS(conn->inbound_delivery));
12791283
qd_message_set_receive_complete(conn->inbound_stream);
1284+
qd_message_cancel_producer_activation(conn->inbound_stream);
12801285
qdr_delivery_continue(tcplite_context->core, conn->inbound_delivery, false);
12811286
qdr_delivery_set_context(conn->inbound_delivery, 0);
1282-
qdr_delivery_decref(tcplite_context->core, conn->inbound_delivery, "TCP_LSIDE_IO - read-close");
1287+
qdr_delivery_decref(tcplite_context->core, conn->inbound_delivery, "FLOW_XSIDE_IO - inbound_delivery released");
12831288
conn->inbound_delivery = 0;
12841289
conn->inbound_stream = 0;
12851290
return true;
@@ -1346,10 +1351,14 @@ static bool manage_flow_XSIDE_IO(tcplite_connection_t *conn)
13461351
// payload has been consumed and written before write-closing the connection.
13471352
//
13481353
if (qd_message_receive_complete(conn->outbound_stream) && !qd_message_can_consume_buffers(conn->outbound_stream)) {
1349-
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, "[C%"PRIu64"] Rx-complete, rings empty: Write-closing the raw connection", conn->conn_id);
1354+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, DLV_FMT " Rx-complete, rings empty: Write-closing the raw connection, consumer activation cancelled",
1355+
DLV_ARGS(conn->outbound_delivery));
13501356
pn_raw_connection_write_close(conn->raw_conn);
1357+
qd_message_set_send_complete(conn->outbound_stream);
1358+
qd_message_cancel_consumer_activation(conn->outbound_stream);
13511359
qdr_delivery_set_context(conn->outbound_delivery, 0);
13521360
qdr_delivery_remote_state_updated(tcplite_context->core, conn->outbound_delivery, PN_ACCEPTED, true, 0, true); // accepted, settled, ref_given
1361+
// do NOT decref outbound_delivery - ref count passed to qdr_delivery_remote_state_updated()!
13531362
conn->outbound_delivery = 0;
13541363
conn->outbound_stream = 0;
13551364
} else {
@@ -1520,15 +1529,10 @@ static bool manage_tls_flow_XSIDE_IO(tcplite_connection_t *conn)
15201529
//
15211530
bool ignore;
15221531
if (qd_tls_is_input_drained(conn->tls, &ignore) && conn->inbound_stream) {
1523-
qd_message_activation_t activation;
1524-
activation.type = QD_ACTIVATION_NONE;
1525-
activation.delivery = 0;
1526-
qd_nullify_safe_ptr(&activation.safeptr);
1527-
1528-
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, DLV_FMT " TLS inbound stream receive complete", DLV_ARGS(conn->inbound_delivery));
1532+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, DLV_FMT " TLS inbound stream receive complete, producer activation cancelled", DLV_ARGS(conn->inbound_delivery));
15291533

15301534
qd_message_set_receive_complete(conn->inbound_stream);
1531-
qd_message_set_producer_activation(conn->inbound_stream, &activation);
1535+
qd_message_cancel_producer_activation(conn->inbound_stream);
15321536

15331537
qdr_delivery_set_context(conn->inbound_delivery, 0);
15341538
qdr_delivery_continue(tcplite_context->core, conn->inbound_delivery, false);
@@ -1541,21 +1545,16 @@ static bool manage_tls_flow_XSIDE_IO(tcplite_connection_t *conn)
15411545
// Check for end of outbound message data
15421546
//
15431547
if (qd_tls_is_output_flushed(conn->tls) && conn->outbound_stream) {
1544-
qd_message_activation_t activation;
1545-
activation.type = QD_ACTIVATION_NONE;
1546-
activation.delivery = 0;
1547-
qd_nullify_safe_ptr(&activation.safeptr);
1548-
1549-
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, DLV_FMT " TLS outbound stream send complete", DLV_ARGS(conn->outbound_delivery));
1548+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, DLV_FMT " TLS outbound stream send complete, consumer activation cancelled", DLV_ARGS(conn->outbound_delivery));
15501549

15511550
qd_message_set_send_complete(conn->outbound_stream);
1552-
qd_message_set_consumer_activation(conn->outbound_stream, &activation);
1551+
qd_message_cancel_consumer_activation(conn->outbound_stream);
15531552

15541553
qdr_delivery_set_context(conn->outbound_delivery, 0);
15551554
qdr_delivery_remote_state_updated(tcplite_context->core, conn->outbound_delivery,
15561555
tls_status < 0 ? PN_MODIFIED : PN_ACCEPTED,
1557-
true, 0, false); // settled, 0, ref_given
1558-
qdr_delivery_decref(tcplite_context->core, conn->outbound_delivery, "TLS_FLOW_XSIDE_IO - outbound_delivery released");
1556+
true, 0, true); // settled, 0, ref_given
1557+
// do NOT decref outbound_delivery - ref count passed to qdr_delivery_remote_state_updated()!
15591558
conn->outbound_delivery = 0;
15601559
conn->outbound_stream = 0;
15611560
}

src/cutthrough_utils.c

+5-12
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
#include "adaptors/tcp_lite/tcp_lite.h"
2626

2727

28-
static void activate_connection(qd_message_activation_t *activation, qd_direction_t dir)
28+
static void activate_connection(const qd_message_activation_t *activation, qd_direction_t dir)
2929
{
3030
switch (activation->type) {
3131
case QD_ACTIVATION_NONE:
@@ -77,20 +77,13 @@ static void activate_connection(qd_message_activation_t *activation, qd_directio
7777
}
7878

7979

80-
void cutthrough_notify_buffers_produced_inbound(qd_message_t *msg)
80+
void cutthrough_notify_buffers_produced_inbound(const qd_message_activation_t *activation)
8181
{
82-
qd_message_activation_t activation;
83-
qd_message_get_consumer_activation(msg, &activation);
84-
activate_connection(&activation, QD_OUTGOING);
82+
activate_connection(activation, QD_OUTGOING);
8583
}
8684

8785

88-
void cutthrough_notify_buffers_consumed_outbound(qd_message_t *msg)
86+
void cutthrough_notify_buffers_consumed_outbound(const qd_message_activation_t *activation)
8987
{
90-
bool unstall = qd_message_resume_from_stalled(msg);
91-
if (unstall) {
92-
qd_message_activation_t activation;
93-
qd_message_get_producer_activation(msg, &activation);
94-
activate_connection(&activation, QD_INCOMING);
95-
}
88+
activate_connection(activation, QD_INCOMING);
9689
}

0 commit comments

Comments
 (0)