1818
1919struct test_backend_ctx {
2020 int invalidate_calls ;
21+ int destroy_calls ;
2122};
2223
2324static void test_session_invalidate (void * session )
@@ -29,6 +30,46 @@ static void test_session_invalidate(void *session)
2930 }
3031}
3132
33+ static int test_session_destroy (void * session )
34+ {
35+ struct test_backend_ctx * ctx = session ;
36+
37+ if (ctx != NULL ) {
38+ ctx -> destroy_calls ++ ;
39+ }
40+
41+ return 0 ;
42+ }
43+
44+ static int setup_conn (struct flb_connection * conn ,
45+ struct flb_upstream * upstream ,
46+ struct flb_config * config ,
47+ flb_pipefd_t * socket_pair )
48+ {
49+ if (flb_pipe_create (socket_pair ) != 0 ) {
50+ return -1 ;
51+ }
52+
53+ config -> is_shutting_down = FLB_FALSE ;
54+ upstream -> base .config = config ;
55+ upstream -> base .net .keepalive = FLB_FALSE ;
56+ upstream -> tcp_host = "example" ;
57+ upstream -> tcp_port = 443 ;
58+ flb_upstream_queue_init (& upstream -> queue );
59+
60+ conn -> fd = socket_pair [0 ];
61+ conn -> event .fd = conn -> fd ;
62+ conn -> event .status = 0 ;
63+ conn -> stream = (struct flb_stream * ) upstream ;
64+ conn -> net = & upstream -> base .net ;
65+ conn -> net_error = 0 ;
66+
67+ mk_list_init (& conn -> _head );
68+ mk_list_add (& conn -> _head , & upstream -> queue .busy_queue );
69+
70+ return 0 ;
71+ }
72+
3273void test_prepare_destroy_conn_marks_tls_session_stale (void )
3374{
3475 struct test_backend_ctx backend_session = {0 };
@@ -38,53 +79,28 @@ void test_prepare_destroy_conn_marks_tls_session_stale(void)
3879 struct flb_connection conn = {0 };
3980 struct flb_upstream upstream = {0 };
4081 struct flb_config config = {0 };
41- struct flb_upstream_queue * queue ;
4282 flb_pipefd_t socket_pair [2 ];
43- int ret ;
4483
4584#ifdef FLB_SYSTEM_WINDOWS
4685 WSADATA wsa_data ;
47-
4886 WSAStartup (0x0201 , & wsa_data );
4987#endif
5088
51- ret = flb_pipe_create (socket_pair );
52- TEST_CHECK (ret == 0 );
89+ TEST_CHECK (setup_conn (& conn , & upstream , & config , socket_pair ) == 0 );
5390
5491 backend_api .session_invalidate = test_session_invalidate ;
5592 tls_context .api = & backend_api ;
56-
5793 tls_session .ptr = & backend_session ;
5894 tls_session .tls = & tls_context ;
5995 tls_session .connection = & conn ;
60-
61- config .is_shutting_down = FLB_FALSE ;
62- upstream .base .config = & config ;
63- upstream .base .net .keepalive = FLB_FALSE ;
64- upstream .tcp_host = "example" ;
65- upstream .tcp_port = 443 ;
66-
67- flb_upstream_queue_init (& upstream .queue );
68-
69- conn .fd = socket_pair [0 ];
70- conn .event .fd = conn .fd ;
71- conn .event .status = 0 ;
72- conn .stream = (struct flb_stream * ) & upstream ;
73- conn .net = & upstream .base .net ;
7496 conn .tls_session = & tls_session ;
75- conn .net_error = 0 ;
76-
77- mk_list_init (& conn ._head );
78- queue = & upstream .queue ;
79- mk_list_add (& conn ._head , & queue -> busy_queue );
8097
81- ret = flb_upstream_conn_release (& conn );
82- TEST_CHECK (ret == 0 );
98+ TEST_CHECK (flb_upstream_conn_release (& conn ) == 0 );
8399
84100 TEST_CHECK (backend_session .invalidate_calls == 1 );
85101 TEST_CHECK (conn .fd == -1 );
86102 TEST_CHECK (conn .event .fd == -1 );
87- TEST_CHECK (mk_list_size (& queue -> destroy_queue ) == 1 );
103+ TEST_CHECK (mk_list_size (& upstream . queue . destroy_queue ) == 1 );
88104 TEST_CHECK (conn .shutdown_flag == FLB_TRUE );
89105
90106 flb_pipe_close (socket_pair [1 ]);
@@ -94,11 +110,63 @@ void test_prepare_destroy_conn_marks_tls_session_stale(void)
94110#endif
95111}
96112
113+ void test_tls_session_destroy_no_double_free (void )
114+ {
115+ struct test_backend_ctx backend_session = {0 };
116+ struct flb_tls_backend backend_api = {0 };
117+ struct flb_tls tls_context = {0 };
118+ struct flb_tls_session * tls_session ;
119+ struct flb_connection * conn ;
120+ struct flb_upstream upstream = {0 };
121+ struct flb_config config = {0 };
122+ flb_pipefd_t socket_pair [2 ];
123+
124+ #ifdef FLB_SYSTEM_WINDOWS
125+ WSADATA wsa_data ;
126+ WSAStartup (0x0201 , & wsa_data );
127+ #endif
128+
129+ /* heap-allocate conn to match production; pending_destroy calls flb_free on it */
130+ conn = flb_calloc (1 , sizeof (struct flb_connection ));
131+ TEST_CHECK (conn != NULL );
132+ conn -> dynamically_allocated = FLB_TRUE ;
133+ TEST_CHECK (setup_conn (conn , & upstream , & config , socket_pair ) == 0 );
134+
135+ backend_api .session_invalidate = test_session_invalidate ;
136+ backend_api .session_destroy = test_session_destroy ;
137+ tls_context .api = & backend_api ;
138+
139+ /* heap-allocated to match production; flb_tls_session_destroy calls flb_free */
140+ tls_session = flb_calloc (1 , sizeof (struct flb_tls_session ));
141+ TEST_CHECK (tls_session != NULL );
142+ tls_session -> ptr = & backend_session ;
143+ tls_session -> tls = & tls_context ;
144+ tls_session -> connection = conn ;
145+ conn -> tls_session = tls_session ;
146+
147+ /* explicit destroy before release — the fix */
148+ TEST_CHECK (flb_tls_session_destroy (tls_session ) == 0 );
149+ TEST_CHECK (conn -> tls_session == NULL );
150+
151+ TEST_CHECK (flb_upstream_conn_release (conn ) == 0 );
152+
153+ /* pending_destroy must not double-free the already-destroyed session */
154+ TEST_CHECK (flb_upstream_conn_pending_destroy (& upstream ) == 0 );
155+ TEST_CHECK (backend_session .destroy_calls == 1 );
156+
157+ flb_pipe_close (socket_pair [1 ]);
158+
159+ #ifdef FLB_SYSTEM_WINDOWS
160+ WSACleanup ();
161+ #endif
162+ }
163+
97164#endif
98165
99166TEST_LIST = {
100167#ifdef FLB_HAVE_TLS
101168 {"prepare_destroy_conn_marks_tls_session_stale" , test_prepare_destroy_conn_marks_tls_session_stale },
169+ {"tls_session_destroy_no_double_free" , test_tls_session_destroy_no_double_free },
102170#endif
103171 {0 }
104172};
0 commit comments