--- /dev/null
+ o Minor bugfixes (hidden service v3):
+ - Fix a race between the circuit close and free where the service would
+ launch a new intro circuit after the close, and then fail to register it
+ before the free of the previously closed circuit. This was making the
+ service unable to find the established intro circuit and thus not upload
+ its descriptor. It can make a service unavailable for up to 24 hours.
+ Fixes bug 23603; bugfix on 0.3.2.1-alpha.
#include "main.h"
#include "hs_circuit.h"
#include "hs_circuitmap.h"
-#include "hs_common.h"
#include "hs_ident.h"
#include "networkstatus.h"
#include "nodelist.h"
circuit_clear_testing_cell_stats(circ);
+ /* Cleanup circuit from anything HS v3 related. We also do this when the
+ * circuit is closed. This is to avoid any code path that free registered
+ * circuits without closing them before. This needs to be done before the
+ * hs identifier is freed. */
+ hs_circ_cleanup(circ);
+
if (CIRCUIT_IS_ORIGIN(circ)) {
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
mem = ocirc;
crypto_pk_free(ocirc->intro_key);
rend_data_free(ocirc->rend_data);
+
+ /* Finally, free the identifier of the circuit and nullify it so multiple
+ * cleanup will work. */
hs_ident_circuit_free(ocirc->hs_ident);
+ ocirc->hs_ident = NULL;
tor_free(ocirc->dest_address);
if (ocirc->socks_username) {
/* Remove from map. */
circuit_set_n_circid_chan(circ, 0, NULL);
- /* Clear HS circuitmap token from this circ (if any) */
- if (circ->hs_token) {
- hs_circuitmap_remove_circuit(circ);
- }
-
/* Clear cell queue _after_ removing it from the map. Otherwise our
* "active" checks will be violated. */
cell_queue_clear(&circ->n_chan_cells);
}
}
+ /* Notify the HS subsystem that this circuit is closing. */
+ hs_circ_cleanup(circ);
+
if (circuits_pending_close == NULL)
circuits_pending_close = smartlist_new();
orig_reason);
}
- /* Notify the HS subsystem for any intro point circuit closing so it can be
- * dealt with cleanly. */
- if (circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
- circ->purpose == CIRCUIT_PURPOSE_S_INTRO) {
- hs_service_intro_circ_has_closed(TO_ORIGIN_CIRCUIT(circ));
- }
-
if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
int timed_out = (reason == END_CIRC_REASON_TIMEOUT);
return -1;
}
+/* We are about to close or free this <b>circ</b>. Clean it up from any
+ * related HS data structures. This function can be called multiple times
+ * safely for the same circuit. */
+void
+hs_circ_cleanup(circuit_t *circ)
+{
+ tor_assert(circ);
+
+ /* If it's a service-side intro circ, notify the HS subsystem for the intro
+ * point circuit closing so it can be dealt with cleanly. */
+ if (circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
+ circ->purpose == CIRCUIT_PURPOSE_S_INTRO) {
+ hs_service_intro_circ_has_closed(TO_ORIGIN_CIRCUIT(circ));
+ }
+
+ /* Clear HS circuitmap token for this circ (if any). Very important to be
+ * done after the HS subsystem has been notified of the close else the
+ * circuit will not be found.
+ *
+ * We do this at the close if possible because from that point on, the
+ * circuit is good as dead. We can't rely on removing it in the circuit
+ * free() function because we open a race window between the close and free
+ * where we can't register a new circuit for the same intro point. */
+ if (circ->hs_token) {
+ hs_circuitmap_remove_circuit(circ);
+ }
+}
+