diff options
author | Christian Grothoff <grothoff@gnunet.org> | 2024-05-08 14:52:39 +0200 |
---|---|---|
committer | Christian Grothoff <grothoff@gnunet.org> | 2024-05-08 14:52:39 +0200 |
commit | db2eb78ec9bade0c6779bdd76a49021a900fcd6b (patch) | |
tree | c3d4cc01c3c31af0a4b35b373dd8dd042a1c751c | |
parent | 75c6debd321c68e0d5010d16ed4c362e3318e3f4 (diff) | |
download | gnunet-db2eb78ec9bade0c6779bdd76a49021a900fcd6b.tar.gz gnunet-db2eb78ec9bade0c6779bdd76a49021a900fcd6b.zip |
UTIL: fix #8786 (use-after-free during shutdown)
-rw-r--r-- | src/lib/util/service.c | 119 |
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 | */ |
355 | static enum GNUNET_GenericReturnValue | 355 | static enum GNUNET_GenericReturnValue |
356 | have_non_monitor_clients (struct GNUNET_SERVICE_Handle *sh) | 356 | have_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 | */ | ||
1902 | static void | ||
1903 | finish_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 | |||
1895 | void | 1937 | void |
1896 | GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Handle *srv) | 1938 | GNUNET_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 | */ | ||
2441 | static void | ||
2442 | finish_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 | |||
2472 | void | 2483 | void |
2473 | GNUNET_SERVICE_client_drop (struct GNUNET_SERVICE_Client *c) | 2484 | GNUNET_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) | |||
2529 | void | 2532 | void |
2530 | GNUNET_SERVICE_shutdown (struct GNUNET_SERVICE_Handle *sh) | 2533 | GNUNET_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 | ||