aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <grothoff@gnunet.org>2024-05-08 14:52:39 +0200
committerChristian Grothoff <grothoff@gnunet.org>2024-05-08 14:52:39 +0200
commitdb2eb78ec9bade0c6779bdd76a49021a900fcd6b (patch)
treec3d4cc01c3c31af0a4b35b373dd8dd042a1c751c
parent75c6debd321c68e0d5010d16ed4c362e3318e3f4 (diff)
downloadgnunet-db2eb78ec9bade0c6779bdd76a49021a900fcd6b.tar.gz
gnunet-db2eb78ec9bade0c6779bdd76a49021a900fcd6b.zip
UTIL: fix #8786 (use-after-free during shutdown)
-rw-r--r--src/lib/util/service.c119
1 files changed, 63 insertions, 56 deletions
diff --git a/src/lib/util/service.c b/src/lib/util/service.c
index 7ed61919f..363210c61 100644
--- a/src/lib/util/service.c
+++ b/src/lib/util/service.c
@@ -350,7 +350,7 @@ struct GNUNET_SERVICE_Client
350 * monitoring. 350 * monitoring.
351 * 351 *
352 * @param sh service to check clients for 352 * @param sh service to check clients for
353 * @return #GNUNET_YES if we have non-monitoring clients left 353 * @return true if we have non-monitoring clients left
354 */ 354 */
355static enum GNUNET_GenericReturnValue 355static enum GNUNET_GenericReturnValue
356have_non_monitor_clients (struct GNUNET_SERVICE_Handle *sh) 356have_non_monitor_clients (struct GNUNET_SERVICE_Handle *sh)
@@ -359,11 +359,13 @@ have_non_monitor_clients (struct GNUNET_SERVICE_Handle *sh)
359 NULL != client; 359 NULL != client;
360 client = client->next) 360 client = client->next)
361 { 361 {
362 if (NULL != client->drop_task)
363 continue;
362 if (client->is_monitor) 364 if (client->is_monitor)
363 continue; 365 continue;
364 return GNUNET_YES; 366 return true;
365 } 367 }
366 return GNUNET_NO; 368 return false;
367} 369}
368 370
369 371
@@ -419,7 +421,7 @@ service_shutdown (void *cls)
419 case GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN: 421 case GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN:
420 if (0 == (sh->suspend_state & SUSPEND_STATE_SHUTDOWN)) 422 if (0 == (sh->suspend_state & SUSPEND_STATE_SHUTDOWN))
421 do_suspend (sh, SUSPEND_STATE_SHUTDOWN); 423 do_suspend (sh, SUSPEND_STATE_SHUTDOWN);
422 if (GNUNET_NO == have_non_monitor_clients (sh)) 424 if (! have_non_monitor_clients (sh))
423 GNUNET_SERVICE_shutdown (sh); 425 GNUNET_SERVICE_shutdown (sh);
424 break; 426 break;
425 } 427 }
@@ -1892,6 +1894,46 @@ GNUNET_SERVICE_start (const char *service_name,
1892} 1894}
1893 1895
1894 1896
1897/**
1898 * Asynchronously finish dropping the client.
1899 *
1900 * @param cls the `struct GNUNET_SERVICE_Client`.
1901 */
1902static void
1903finish_client_drop (void *cls)
1904{
1905 struct GNUNET_SERVICE_Client *c = cls;
1906 struct GNUNET_SERVICE_Handle *sh = c->sh;
1907
1908 c->drop_task = NULL;
1909 GNUNET_CONTAINER_DLL_remove (sh->clients_head,
1910 sh->clients_tail,
1911 c);
1912 GNUNET_assert (NULL == c->send_task);
1913 GNUNET_assert (NULL == c->recv_task);
1914 GNUNET_assert (NULL == c->warn_task);
1915 GNUNET_MST_destroy (c->mst);
1916 GNUNET_MQ_destroy (c->mq);
1917 if (! c->persist)
1918 {
1919 GNUNET_break (GNUNET_OK ==
1920 GNUNET_NETWORK_socket_close (c->sock));
1921 if ((0 != (SUSPEND_STATE_EMFILE & sh->suspend_state)) &&
1922 (0 == (SUSPEND_STATE_SHUTDOWN & sh->suspend_state)))
1923 do_resume (sh,
1924 SUSPEND_STATE_EMFILE);
1925 }
1926 else
1927 {
1928 GNUNET_NETWORK_socket_free_memory_only_ (c->sock);
1929 }
1930 GNUNET_free (c);
1931 if ((0 != (SUSPEND_STATE_SHUTDOWN & sh->suspend_state)) &&
1932 (! have_non_monitor_clients (sh)))
1933 GNUNET_SERVICE_shutdown (sh);
1934}
1935
1936
1895void 1937void
1896GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Handle *srv) 1938GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Handle *srv)
1897{ 1939{
@@ -1899,7 +1941,12 @@ GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Handle *srv)
1899 1941
1900 GNUNET_SERVICE_suspend (srv); 1942 GNUNET_SERVICE_suspend (srv);
1901 while (NULL != (client = srv->clients_head)) 1943 while (NULL != (client = srv->clients_head))
1902 GNUNET_SERVICE_client_drop (client); 1944 {
1945 if (NULL == client->drop_task)
1946 GNUNET_SERVICE_client_drop (client);
1947 GNUNET_SCHEDULER_cancel (client->drop_task);
1948 finish_client_drop (client);
1949 }
1903 teardown_service (srv); 1950 teardown_service (srv);
1904 GNUNET_free (srv->handlers); 1951 GNUNET_free (srv->handlers);
1905 GNUNET_free (srv); 1952 GNUNET_free (srv);
@@ -2433,42 +2480,6 @@ GNUNET_SERVICE_client_disable_continue_warning (struct GNUNET_SERVICE_Client *c)
2433} 2480}
2434 2481
2435 2482
2436/**
2437 * Asynchronously finish dropping the client.
2438 *
2439 * @param cls the `struct GNUNET_SERVICE_Client`.
2440 */
2441static void
2442finish_client_drop (void *cls)
2443{
2444 struct GNUNET_SERVICE_Client *c = cls;
2445 struct GNUNET_SERVICE_Handle *sh = c->sh;
2446
2447 c->drop_task = NULL;
2448 GNUNET_assert (NULL == c->send_task);
2449 GNUNET_assert (NULL == c->recv_task);
2450 GNUNET_assert (NULL == c->warn_task);
2451 GNUNET_MST_destroy (c->mst);
2452 GNUNET_MQ_destroy (c->mq);
2453 if (! c->persist)
2454 {
2455 GNUNET_break (GNUNET_OK ==
2456 GNUNET_NETWORK_socket_close (c->sock));
2457 if ((0 != (SUSPEND_STATE_EMFILE & sh->suspend_state)) &&
2458 (0 == (SUSPEND_STATE_SHUTDOWN & sh->suspend_state)))
2459 do_resume (sh, SUSPEND_STATE_EMFILE);
2460 }
2461 else
2462 {
2463 GNUNET_NETWORK_socket_free_memory_only_ (c->sock);
2464 }
2465 GNUNET_free (c);
2466 if ((0 != (SUSPEND_STATE_SHUTDOWN & sh->suspend_state)) &&
2467 (GNUNET_NO == have_non_monitor_clients (sh)))
2468 GNUNET_SERVICE_shutdown (sh);
2469}
2470
2471
2472void 2483void
2473GNUNET_SERVICE_client_drop (struct GNUNET_SERVICE_Client *c) 2484GNUNET_SERVICE_client_drop (struct GNUNET_SERVICE_Client *c)
2474{ 2485{
@@ -2493,15 +2504,7 @@ GNUNET_SERVICE_client_drop (struct GNUNET_SERVICE_Client *c)
2493 backtrace_strings[i]); 2504 backtrace_strings[i]);
2494 } 2505 }
2495#endif 2506#endif
2496 if (NULL != c->drop_task) 2507 GNUNET_assert (NULL == c->drop_task);
2497 {
2498 /* asked to drop twice! */
2499 GNUNET_assert (0);
2500 return;
2501 }
2502 GNUNET_CONTAINER_DLL_remove (sh->clients_head,
2503 sh->clients_tail,
2504 c);
2505 if (NULL != sh->disconnect_cb) 2508 if (NULL != sh->disconnect_cb)
2506 sh->disconnect_cb (sh->cb_cls, 2509 sh->disconnect_cb (sh->cb_cls,
2507 c, 2510 c,
@@ -2529,12 +2532,16 @@ GNUNET_SERVICE_client_drop (struct GNUNET_SERVICE_Client *c)
2529void 2532void
2530GNUNET_SERVICE_shutdown (struct GNUNET_SERVICE_Handle *sh) 2533GNUNET_SERVICE_shutdown (struct GNUNET_SERVICE_Handle *sh)
2531{ 2534{
2532 struct GNUNET_SERVICE_Client *client;
2533
2534 if (0 == (sh->suspend_state & SUSPEND_STATE_SHUTDOWN)) 2535 if (0 == (sh->suspend_state & SUSPEND_STATE_SHUTDOWN))
2535 do_suspend (sh, SUSPEND_STATE_SHUTDOWN); 2536 do_suspend (sh,
2536 while (NULL != (client = sh->clients_head)) 2537 SUSPEND_STATE_SHUTDOWN);
2537 GNUNET_SERVICE_client_drop (client); 2538 for (struct GNUNET_SERVICE_Client *client = sh->clients_head;
2539 NULL != client;
2540 client = client->next)
2541 {
2542 if (NULL == client->drop_task)
2543 GNUNET_SERVICE_client_drop (client);
2544 }
2538} 2545}
2539 2546
2540 2547
@@ -2543,7 +2550,7 @@ GNUNET_SERVICE_client_mark_monitor (struct GNUNET_SERVICE_Client *c)
2543{ 2550{
2544 c->is_monitor = true; 2551 c->is_monitor = true;
2545 if (((0 != (SUSPEND_STATE_SHUTDOWN & c->sh->suspend_state)) && 2552 if (((0 != (SUSPEND_STATE_SHUTDOWN & c->sh->suspend_state)) &&
2546 (GNUNET_NO == have_non_monitor_clients (c->sh)))) 2553 (! have_non_monitor_clients (c->sh))))
2547 GNUNET_SERVICE_shutdown (c->sh); 2554 GNUNET_SERVICE_shutdown (c->sh);
2548} 2555}
2549 2556