tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

commit b3eb133de68e8ce77fbf382e0df25ed523cd7f44
parent a40b58b3c9dfc45886d5213dda5a02701fbeaeeb
Author: Alexander Hansen Færøy <ahf@torproject.org>
Date:   Wed, 28 Jan 2026 16:58:03 +0100

Merge branch 'pt-death' into 'main'

transports: handle process death better

See merge request tpo/core/tor!977
Diffstat:
Msrc/feature/client/transports.c | 48++++++++++++++++++++++++++++++++++++++++++------
Msrc/feature/client/transports.h | 5+++++
2 files changed, 47 insertions(+), 6 deletions(-)

diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c @@ -90,6 +90,7 @@ **/ #include "lib/string/printf.h" +#include "lib/evloop/compat_libevent.h" #define PT_PRIVATE #include "core/or/or.h" #include "feature/client/bridges.h" @@ -492,7 +493,9 @@ proxy_needs_restart(const managed_proxy_t *mp) launched: */ tor_assert(smartlist_len(mp->transports_to_launch) > 0); - tor_assert(mp->conf_state == PT_PROTO_COMPLETED); + if (BUG(mp->conf_state != PT_PROTO_COMPLETED)) { + goto needs_restart; + } if (smartlist_len(mp->transports_to_launch) != smartlist_len(mp->transports)) goto needs_restart; @@ -517,7 +520,12 @@ proxy_prepare_for_restart(managed_proxy_t *mp) { transport_t *t_tmp = NULL; - tor_assert(mp->conf_state == PT_PROTO_COMPLETED); + /* Rate limit this log as a regurlarly dying PT would log this once every + * second (retry time). Every 5 minutes is likely loud enough to notice. */ + static ratelim_t log_died_lim = RATELIM_INIT(300); + log_fn_ratelim(&log_died_lim, LOG_WARN, LD_PT, + "Managed proxy at '%s' died in state %s", mp->argv[0], + managed_proxy_state_to_string(mp->conf_state)); /* destroy the process handle and terminate the process. */ if (mp->process) { @@ -543,9 +551,11 @@ proxy_prepare_for_restart(managed_proxy_t *mp) mp->proxy_uri = get_pt_proxy_uri(); mp->proxy_supported = 0; + if (mp->conf_state == PT_PROTO_COMPLETED) + unconfigured_proxies_n++; + /* flag it as an infant proxy so that it gets launched on next tick */ managed_proxy_set_state(mp, PT_PROTO_INFANT); - unconfigured_proxies_n++; } /** Launch managed proxy <b>mp</b>. */ @@ -643,6 +653,23 @@ pt_configure_remaining_proxies(void) mark_my_descriptor_dirty("configured managed proxies"); } +/** event callback to launch managed proxy after a delay */ +STATIC void +launch_proxy_ev(mainloop_event_t *event, void *v) +{ + managed_proxy_t *mp = v; + + (void) event; + + tor_assert(mp); + tor_assert(mp->conf_state == PT_PROTO_WAITING); + + if (launch_managed_proxy(mp) < 0) { /* launch fail */ + managed_proxy_set_state(mp, PT_PROTO_FAILED_LAUNCH); + handle_finished_proxy(mp); + } +} + /** Attempt to continue configuring managed proxy <b>mp</b>. * Return 1 if the transport configuration finished, and return 0 * otherwise (if we still have more configuring to do for this @@ -652,10 +679,13 @@ configure_proxy(managed_proxy_t *mp) { /* if we haven't launched the proxy yet, do it now */ if (mp->conf_state == PT_PROTO_INFANT) { - if (launch_managed_proxy(mp) < 0) { /* launch fail */ - managed_proxy_set_state(mp, PT_PROTO_FAILED_LAUNCH); - handle_finished_proxy(mp); + const struct timeval delay_tv = { 1, 0 }; + if (!mp->process_launch_ev) { + mp->process_launch_ev = mainloop_event_new(launch_proxy_ev, mp); } + mainloop_event_schedule(mp->process_launch_ev, &delay_tv); + managed_proxy_set_state(mp, PT_PROTO_WAITING); + return 0; } @@ -756,6 +786,9 @@ managed_proxy_destroy(managed_proxy_t *mp, process_terminate(mp->process); } + if (mp->process_launch_ev) + mainloop_event_free(mp->process_launch_ev); + tor_free(mp); } @@ -828,6 +861,7 @@ handle_finished_proxy(managed_proxy_t *mp) managed_proxy_set_state(mp, PT_PROTO_COMPLETED); break; case PT_PROTO_INFANT: + case PT_PROTO_WAITING: case PT_PROTO_LAUNCHED: case PT_PROTO_ACCEPTING_METHODS: case PT_PROTO_COMPLETED: @@ -2205,6 +2239,8 @@ managed_proxy_state_to_string(enum pt_proto_state state) switch (state) { case PT_PROTO_INFANT: return "Infant"; + case PT_PROTO_WAITING: + return "Waiting"; case PT_PROTO_LAUNCHED: return "Launched"; case PT_PROTO_ACCEPTING_METHODS: diff --git a/src/feature/client/transports.h b/src/feature/client/transports.h @@ -77,6 +77,7 @@ char *tor_escape_str_for_pt_args(const char *string, /** State of the managed proxy configuration protocol. */ enum pt_proto_state { PT_PROTO_INFANT, /* was just born */ + PT_PROTO_WAITING, /* waiting to be launched */ PT_PROTO_LAUNCHED, /* was just launched */ PT_PROTO_ACCEPTING_METHODS, /* accepting methods */ PT_PROTO_CONFIGURED, /* configured successfully */ @@ -101,6 +102,9 @@ typedef struct { /* A pointer to the process of this managed proxy. */ struct process_t *process; + /* timer event to launch proxy */ + struct mainloop_event_t *process_launch_ev; + /** Boolean: We are re-parsing our config, and we are going to * remove this managed proxy if we don't find it any transport * plugins that use it. */ @@ -150,6 +154,7 @@ STATIC void managed_proxy_destroy(managed_proxy_t *mp, STATIC managed_proxy_t *managed_proxy_create(const smartlist_t *transport_list, char **proxy_argv, int is_server); +STATIC void launch_proxy_ev(struct mainloop_event_t *event, void *v); STATIC int configure_proxy(managed_proxy_t *mp); STATIC char* get_pt_proxy_uri(void);