@@ -45,6 +45,16 @@ int fw_conn_event(void *data)
4545
4646 conn = connection -> user_data ;
4747
48+ /* Check if connection is being deleted, and increment reference count */
49+ pthread_mutex_lock (& conn -> refcount_mutex );
50+ if (conn -> pending_deletion ) {
51+ /* Connection is being deleted, don't process this event */
52+ pthread_mutex_unlock (& conn -> refcount_mutex );
53+ return -1 ;
54+ }
55+ conn -> refcount ++ ;
56+ pthread_mutex_unlock (& conn -> refcount_mutex );
57+
4858 ctx = conn -> ctx ;
4959
5060 event = & connection -> event ;
@@ -56,12 +66,17 @@ int fw_conn_event(void *data)
5666 ret = fw_prot_secure_forward_handshake (ctx -> ins , conn );
5767 if (ret == -1 ) {
5868 flb_plg_trace (ctx -> ins , "fd=%i closed connection" , event -> fd );
69+ pthread_mutex_lock (& conn -> refcount_mutex );
70+ conn -> pending_deletion = 1 ;
71+ pthread_mutex_unlock (& conn -> refcount_mutex );
5972 fw_conn_del (conn );
60-
6173 return -1 ;
6274 }
6375
6476 conn -> handshake_status = FW_HANDSHAKE_ESTABLISHED ;
77+
78+ /* Release event handler's reference */
79+ fw_conn_del (conn );
6580 return 0 ;
6681 }
6782
@@ -72,6 +87,9 @@ int fw_conn_event(void *data)
7287 if (conn -> buf_size >= ctx -> buffer_max_size ) {
7388 flb_plg_warn (ctx -> ins , "fd=%i incoming data exceed limit (%lu bytes)" ,
7489 event -> fd , (ctx -> buffer_max_size ));
90+ pthread_mutex_lock (& conn -> refcount_mutex );
91+ conn -> pending_deletion = 1 ;
92+ pthread_mutex_unlock (& conn -> refcount_mutex );
7593 fw_conn_del (conn );
7694 return -1 ;
7795 }
@@ -86,6 +104,10 @@ int fw_conn_event(void *data)
86104 tmp = flb_realloc (conn -> buf , size );
87105 if (!tmp ) {
88106 flb_errno ();
107+ pthread_mutex_lock (& conn -> refcount_mutex );
108+ conn -> pending_deletion = 1 ;
109+ pthread_mutex_unlock (& conn -> refcount_mutex );
110+ fw_conn_del (conn );
89111 return -1 ;
90112 }
91113 flb_plg_trace (ctx -> ins , "fd=%i buffer realloc %i -> %i" ,
@@ -106,24 +128,40 @@ int fw_conn_event(void *data)
106128 conn -> buf_len += bytes ;
107129
108130 ret = fw_prot_process (ctx -> ins , conn );
131+
109132 if (ret == -1 ) {
133+ pthread_mutex_lock (& conn -> refcount_mutex );
134+ conn -> pending_deletion = 1 ;
135+ pthread_mutex_unlock (& conn -> refcount_mutex );
110136 fw_conn_del (conn );
111137 return -1 ;
112138 }
139+
140+ /* Release event handler's reference */
141+ fw_conn_del (conn );
113142 return bytes ;
114143 }
115144 else {
116145 flb_plg_trace (ctx -> ins , "fd=%i closed connection" , event -> fd );
146+ pthread_mutex_lock (& conn -> refcount_mutex );
147+ conn -> pending_deletion = 1 ;
148+ pthread_mutex_unlock (& conn -> refcount_mutex );
117149 fw_conn_del (conn );
118150 return -1 ;
119151 }
120152 }
121153
122154 if (event -> mask & MK_EVENT_CLOSE ) {
123155 flb_plg_trace (ctx -> ins , "fd=%i hangup" , event -> fd );
156+ pthread_mutex_lock (& conn -> refcount_mutex );
157+ conn -> pending_deletion = 1 ;
158+ pthread_mutex_unlock (& conn -> refcount_mutex );
124159 fw_conn_del (conn );
125160 return -1 ;
126161 }
162+
163+ /* Release event handler's reference */
164+ fw_conn_del (conn );
127165 return 0 ;
128166}
129167
@@ -203,6 +241,20 @@ struct fw_conn *fw_conn_add(struct flb_connection *connection, struct flb_in_fw_
203241 conn -> compression_type = FLB_COMPRESSION_ALGORITHM_NONE ;
204242 conn -> d_ctx = NULL ;
205243
244+ /* Initialize reference count to 0 (only counts temporary event handler references) */
245+ conn -> refcount = 0 ;
246+ conn -> pending_deletion = 0 ;
247+ ret = pthread_mutex_init (& conn -> refcount_mutex , NULL );
248+ if (ret != 0 ) {
249+ flb_errno ();
250+ if (conn -> helo != NULL ) {
251+ flb_free (conn -> helo );
252+ }
253+ flb_free (conn -> buf );
254+ flb_free (conn );
255+ return NULL ;
256+ }
257+
206258 /* Register instance into the event loop */
207259 ret = mk_event_add (flb_engine_evl_get (),
208260 connection -> fd ,
@@ -223,8 +275,16 @@ struct fw_conn *fw_conn_add(struct flb_connection *connection, struct flb_in_fw_
223275 return conn ;
224276}
225277
226- int fw_conn_del (struct fw_conn * conn )
278+ /* Helper function to destroy connection resources */
279+ static void fw_conn_destroy (struct fw_conn * conn )
227280{
281+ /* Unregister from event loop first, before freeing anything.
282+ * This prevents any queued events from trying to execute fw_conn_event
283+ * after the connection has been freed, which would cause a use-after-free
284+ * when trying to lock conn->refcount_mutex on line 48 of fw_conn_event.
285+ */
286+ mk_event_del (flb_engine_evl_get (), & conn -> connection -> event );
287+
228288 /* The downstream unregisters the file descriptor from the event-loop
229289 * so there's nothing to be done by the plugin
230290 */
@@ -247,8 +307,27 @@ int fw_conn_del(struct fw_conn *conn)
247307 }
248308 flb_free (conn -> helo );
249309 }
310+
311+ /* Destroy the mutex before freeing the connection */
312+ pthread_mutex_destroy (& conn -> refcount_mutex );
313+
250314 flb_free (conn -> buf );
251315 flb_free (conn );
316+ }
317+
318+ int fw_conn_del (struct fw_conn * conn )
319+ {
320+ int should_free ;
321+
322+ /* Decrement reference count and check if we should free */
323+ pthread_mutex_lock (& conn -> refcount_mutex );
324+ conn -> refcount -- ;
325+ should_free = (conn -> pending_deletion && conn -> refcount == 0 );
326+ pthread_mutex_unlock (& conn -> refcount_mutex );
327+
328+ if (should_free ) {
329+ fw_conn_destroy (conn );
330+ }
252331
253332 return 0 ;
254333}
@@ -258,10 +337,26 @@ int fw_conn_del_all(struct flb_in_fw_config *ctx)
258337 struct mk_list * tmp ;
259338 struct mk_list * head ;
260339 struct fw_conn * conn ;
340+ int should_free ;
261341
262342 mk_list_foreach_safe (head , tmp , & ctx -> connections ) {
263343 conn = mk_list_entry (head , struct fw_conn , _head );
264- fw_conn_del (conn );
344+
345+ /* Mark for deletion and check if we should free immediately */
346+ pthread_mutex_lock (& conn -> refcount_mutex );
347+ if (conn -> pending_deletion ) {
348+ pthread_mutex_unlock (& conn -> refcount_mutex );
349+ continue ;
350+ }
351+ conn -> pending_deletion = 1 ;
352+ should_free = (conn -> refcount == 0 );
353+ pthread_mutex_unlock (& conn -> refcount_mutex );
354+
355+ if (should_free ) {
356+ /* No event handler is currently processing, free immediately */
357+ fw_conn_destroy (conn );
358+ }
359+ /* Otherwise, event handler will see pending_deletion and free when done */
265360 }
266361
267362 return 0 ;
0 commit comments