@@ -23,6 +23,7 @@ void test_nonexistent_binary_cleanup(void);
2323void test_nonexecutable_binary_cleanup (void );
2424void test_crashing_binary_cleanup (void );
2525void test_max_tunnel_slots_enforced (void );
26+ void test_tunnel_timeout_kills_localproxy (void );
2627
2728static int initial_fd_count ;
2829
@@ -135,26 +136,48 @@ void test_nonexecutable_binary_cleanup(void) {
135136 TEST_ASSERT_EQUAL_INT (initial_fd_count , count_open_fds ());
136137}
137138
138- // Test crashing localproxy binary
139- void test_crashing_binary_cleanup (void ) {
140- // Create temp directory with crashing localproxy script
139+ // Test that 21st tunnel is rejected when 20 slots are occupied
140+ void test_max_tunnel_slots_enforced (void ) {
141+ SecureTunnelConfig * config
142+ = make_config_with_max ("/nonexistent" , MAX_TUNNEL_SLOTS + 1 );
143+ uint8_t arena_mem [1024 ];
144+
145+ // Manually occupy all 20 slots
146+ pthread_mutex_lock (& tunnel_mutex );
147+ active_tunnels = MAX_TUNNEL_SLOTS ;
148+ tunnel_slots_mask = 0xFFFFF ; // All 20 bits set
149+ pthread_mutex_unlock (& tunnel_mutex );
150+
151+ GgMap notification
152+ = mock_create_tunnel_notification (arena_mem , sizeof (arena_mem ));
153+ GgError ret = handle_tunnel_notification (notification , config );
154+
155+ TEST_ASSERT_EQUAL (GG_ERR_NOMEM , ret );
156+ TEST_ASSERT_EQUAL_INT (MAX_TUNNEL_SLOTS , active_tunnels );
157+ }
158+
159+ // Test that tunnel timeout kills a long-running localproxy
160+ void test_tunnel_timeout_kills_localproxy (void ) {
161+ // Create a localproxy script that sleeps and logs its PID
141162 mkdir (TEST_DIR , 0755 );
142163 FILE * f = fopen (TEST_DIR "/localproxy" , "w" );
143164 TEST_ASSERT_NOT_NULL (f );
144- fprintf (f , "#!/bin/sh\nkill -SEGV $$ \n" );
165+ fprintf (f , "#!/bin/sh\necho $$ > /tmp/localproxy.pid\nsleep 3600 \n" );
145166 fclose (f );
146167 chmod (TEST_DIR "/localproxy" , 0755 );
147168
148169 SecureTunnelConfig * config = make_config (TEST_DIR );
170+ config -> tunnel_timeout_seconds = 5 ;
171+
149172 uint8_t arena_mem [1024 ];
150173 GgMap notification
151174 = mock_create_tunnel_notification (arena_mem , sizeof (arena_mem ));
152175
153176 GgError ret = handle_tunnel_notification (notification , config );
154177 TEST_ASSERT_EQUAL (GG_ERR_OK , ret );
155178
156- // Wait for worker thread to complete by polling active_tunnels
157- for (int i = 0 ; i < 50 && active_tunnels > 0 ; i ++ ) {
179+ // Wait for timeout to trigger
180+ for (int i = 0 ; i < 70 && active_tunnels > 0 ; i ++ ) {
158181 usleep (100000 ); // 100ms
159182 }
160183
@@ -163,24 +186,35 @@ void test_crashing_binary_cleanup(void) {
163186 TEST_ASSERT_EQUAL_INT (initial_fd_count , count_open_fds ());
164187}
165188
166- // Test that 21st tunnel is rejected when 20 slots are occupied
167- void test_max_tunnel_slots_enforced (void ) {
168- SecureTunnelConfig * config
169- = make_config_with_max ("/nonexistent" , MAX_TUNNEL_SLOTS + 1 );
170- uint8_t arena_mem [1024 ];
171-
172- // Manually occupy all 20 slots
173- pthread_mutex_lock (& tunnel_mutex );
174- active_tunnels = MAX_TUNNEL_SLOTS ;
175- tunnel_slots_mask = 0xFFFFF ; // All 20 bits set
176- pthread_mutex_unlock (& tunnel_mutex );
189+ // Test crashing localproxy binary
190+ void test_crashing_binary_cleanup (void ) {
191+ // Create temp directory with crashing localproxy script
192+ mkdir (TEST_DIR , 0755 );
193+ FILE * f = fopen (TEST_DIR "/localproxy" , "w" );
194+ TEST_ASSERT_NOT_NULL (f );
195+ fprintf (f , "#!/bin/sh\nkill -SEGV $$\n" );
196+ fclose (f );
197+ chmod (TEST_DIR "/localproxy" , 0755 );
177198
199+ SecureTunnelConfig * config = make_config (TEST_DIR );
200+ uint8_t arena_mem [1024 ];
178201 GgMap notification
179202 = mock_create_tunnel_notification (arena_mem , sizeof (arena_mem ));
203+
180204 GgError ret = handle_tunnel_notification (notification , config );
205+ TEST_ASSERT_EQUAL (GG_ERR_OK , ret );
181206
182- TEST_ASSERT_EQUAL (GG_ERR_NOMEM , ret );
183- TEST_ASSERT_EQUAL_INT (MAX_TUNNEL_SLOTS , active_tunnels );
207+ // Wait for timeout to trigger
208+ for (int i = 0 ; i < 40 && active_tunnels > 0 ; i ++ ) {
209+ usleep (100000 ); // 100ms
210+ }
211+
212+ TEST_ASSERT_EQUAL_INT (0 , active_tunnels );
213+ TEST_ASSERT_EQUAL_UINT32 (0 , tunnel_slots_mask );
214+ TEST_ASSERT_EQUAL_INT (initial_fd_count , count_open_fds ());
215+
216+ unlink (TEST_DIR "/localproxy" );
217+ rmdir (TEST_DIR );
184218}
185219
186220int main (void ) {
@@ -189,5 +223,6 @@ int main(void) {
189223 RUN_TEST (test_nonexecutable_binary_cleanup );
190224 RUN_TEST (test_crashing_binary_cleanup );
191225 RUN_TEST (test_max_tunnel_slots_enforced );
226+ RUN_TEST (test_tunnel_timeout_kills_localproxy );
192227 return UNITY_END ();
193228}
0 commit comments