diff options
51 files changed, 373 insertions, 226 deletions
diff --git a/.gitignore b/.gitignore index 77610b3193..f60d72bf1e 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,9 @@ details-*.json uptime-*.json *.full_url *.last_modified +# Core files +core +core.* # / /Makefile diff --git a/changes/40241 b/changes/40241 new file mode 100644 index 0000000000..c9b2e2c011 --- /dev/null +++ b/changes/40241 @@ -0,0 +1,4 @@ + o Minor bugfixes (compilation): + - Fix a compilation warning about unreachable fallthrough annotations + when building with "--enable-all-bugs-are-fatal" on some compilers. + Fixes bug 40241; bugfix on 0.3.5.4-alpha. diff --git a/changes/40241_v2 b/changes/40241_v2 new file mode 100644 index 0000000000..85038297f7 --- /dev/null +++ b/changes/40241_v2 @@ -0,0 +1,4 @@ + o Minor bugfixes (compilation): + - Fix another warning about unreachable fallthrough annotations + when building with "--enable-all-bugs-are-fatal" on some compilers. + Fixes bug 40241; bugfix on 0.4.5.3-rc. diff --git a/changes/bug34400 b/changes/bug34400 new file mode 100644 index 0000000000..e2b56688b9 --- /dev/null +++ b/changes/bug34400 @@ -0,0 +1,5 @@ + o Minor bugfixes (v2 onion services): + - For HSFETCH commands on v2 onion services addresses, check the length of + bytes decoded, not the base32 length. This takes the behavior introduced + in commit a517daa56f5848d25ba79617a1a7b82ed2b0a7c0 into consideration. + Fixes bug 34400; bugfix on 0.4.1.1-alpha. Patch by Neel Chauhan. diff --git a/changes/bug40080 b/changes/bug40080 new file mode 100644 index 0000000000..8162466354 --- /dev/null +++ b/changes/bug40080 @@ -0,0 +1,6 @@ + o Minor bugfixes (security): + - When completing a channel, relays now check more thoroughly to make + sure that it matches any pending circuits before attaching those + circuits. Previously, address correctness and Ed25519 identities were not + checked in this case, but only when extending circuits on an existing + channel. Fixes bug 40080; bugfix on 0.2.7.2-alpha. diff --git a/changes/bug40179_part1 b/changes/bug40179_part1 new file mode 100644 index 0000000000..c302373534 --- /dev/null +++ b/changes/bug40179_part1 @@ -0,0 +1,4 @@ + o Minor bugfixes (testing, portability): + - Fix our Python reference-implementation for the v3 onion service + handshake so that it works correctly with the version of hashlib provided + by Python 3.9. Fixes part of bug 40179; bugfix on 0.3.1.6-rc. diff --git a/changes/bug40179_part2 b/changes/bug40179_part2 new file mode 100644 index 0000000000..15dc861321 --- /dev/null +++ b/changes/bug40179_part2 @@ -0,0 +1,4 @@ + o Minor bugfixes (testing): + - Fix the config/parse_tcp_proxy_line test so that it works correctly on + systems where the DNS provider hijacks invalid queries. + Fixes part of bug 40179; bugfix on 0.4.3.1-alpha. diff --git a/changes/parallel_unit_test b/changes/parallel_unit_test new file mode 100644 index 0000000000..79de28636d --- /dev/null +++ b/changes/parallel_unit_test @@ -0,0 +1,4 @@ + o Minor features (tests): + - Our "make check" target now runs the unit tests in 8 parallel chunks. + Doing this speeds up hardened CI builds by more than a factor of two. + Closes ticket 40098. diff --git a/changes/ticket33747 b/changes/ticket33747 new file mode 100644 index 0000000000..57c72e9d0a --- /dev/null +++ b/changes/ticket33747 @@ -0,0 +1,7 @@ + o Minor bugfixes (rate limiting, bridges, pluggable transports): + - On a bridge, treat all connections from an ExtORPort as remote + by default for the purposes of rate-limiting. Previously, + bridges would treat the connection as local unless they explicitly + received a "USERADDR" command. ExtORPort connections still + count as local if there is a USERADDR command with an explicit local + address. Fixes bug 33747; bugfix on 0.2.5.1-alpha. diff --git a/changes/ticket33880 b/changes/ticket33880 new file mode 100644 index 0000000000..c1889bb134 --- /dev/null +++ b/changes/ticket33880 @@ -0,0 +1,6 @@ + o Minor bugfixes (relay, usability): + - Adjust the rules for when to warn about having too many connections + to other relays. Previously we'd tolerate up to 1.5 connections + per relay on average. Now we tolerate more connections for directory + authorities, and raise the number of total connections we need + to see before we warn. Fixes bug 33880; bugfix on 0.3.1.1-alpha. diff --git a/changes/ticket40003 b/changes/ticket40003 new file mode 100644 index 0000000000..240f464353 --- /dev/null +++ b/changes/ticket40003 @@ -0,0 +1,3 @@ + o Deprecated features (onion service v2): + - Add deprecation warning for onion service version 2. Tor now logs a + warning once if a version 2 service is configured. Closes ticket 40003. diff --git a/changes/ticket40035 b/changes/ticket40035 new file mode 100644 index 0000000000..8cdd447199 --- /dev/null +++ b/changes/ticket40035 @@ -0,0 +1,5 @@ + o Major bugfixes (NSS): + - When running with NSS enabled, make sure that NSS knows to expect + nonblocking sockets. Previously, we set our TCP sockets as blocking, + but did not tell NSS about the fact, which in turn could lead to + unexpected blocking behavior. Fixes bug 40035; bugfix on 0.3.5.1-alpha. diff --git a/changes/ticket40073 b/changes/ticket40073 new file mode 100644 index 0000000000..30b028c042 --- /dev/null +++ b/changes/ticket40073 @@ -0,0 +1,3 @@ + o Minor bugfixes (relay configuration, crash): + - Avoid a fatal assert() when failing to create a listener connection for an + address that was in use. Fixes bug 40073; bugfix on 0.3.5.1-alpha. diff --git a/changes/ticket40081 b/changes/ticket40081 new file mode 100644 index 0000000000..683ae33518 --- /dev/null +++ b/changes/ticket40081 @@ -0,0 +1,6 @@ + o Minor features (security): + - Channels using obsolete versions of the Tor link protocol are no + longer allowed to circumvent address-canonicity checks. + (This is only a minor issue, since such channels have no way to + set ed25519 keys, and therefore should always be rejected.) + Closes ticket 40081. diff --git a/changes/ticket40133 b/changes/ticket40133 new file mode 100644 index 0000000000..8bbe00b6b2 --- /dev/null +++ b/changes/ticket40133 @@ -0,0 +1,5 @@ + o Minor features (protocol simplification): + - Tor no longer allows subprotocol versions larger than 63. Previously + versions up to UINT32_MAX were allowed, which significantly complicated + our code. + Implements proposal 318; closes ticket 40133. diff --git a/configure.ac b/configure.ac index e28f957d03..921e2f197e 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl Copyright (c) 2007-2019, The Tor Project, Inc. dnl See LICENSE for licensing information AC_PREREQ([2.63]) -AC_INIT([tor],[0.4.3.6-dev]) +AC_INIT([tor],[0.4.3.7-dev]) AC_CONFIG_SRCDIR([src/app/main/tor_main.c]) AC_CONFIG_MACRO_DIR([m4]) @@ -16,7 +16,7 @@ configure_flags="$*" # version number changes. Tor uses it to make sure that it # only shuts down for missing "required protocols" when those protocols # are listed as required by a consensus after this date. -AC_DEFINE(APPROX_RELEASE_DATE, ["2020-07-09"], # for 0.4.3.6-dev +AC_DEFINE(APPROX_RELEASE_DATE, ["2020-11-12"], # for 0.4.3.7-dev [Approximate date when this software was released. (Updated when the version changes.)]) # "foreign" means we don't follow GNU package layout standards diff --git a/contrib/win32build/tor-mingw.nsi.in b/contrib/win32build/tor-mingw.nsi.in index f1de58e70f..e3f8be0b86 100644 --- a/contrib/win32build/tor-mingw.nsi.in +++ b/contrib/win32build/tor-mingw.nsi.in @@ -8,7 +8,7 @@ !include "LogicLib.nsh" !include "FileFunc.nsh" !insertmacro GetParameters -!define VERSION "0.4.3.6-dev" +!define VERSION "0.4.3.7-dev" !define INSTALLER "tor-${VERSION}-win32.exe" !define WEBSITE "https://www.torproject.org/" !define LICENSE "LICENSE" diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index 57b48d49f3..fd8c7e37ab 100644 --- a/src/core/mainloop/connection.c +++ b/src/core/mainloop/connection.c @@ -383,8 +383,12 @@ or_connection_new(int type, int socket_family) connection_or_set_canonical(or_conn, 0); - if (type == CONN_TYPE_EXT_OR) + if (type == CONN_TYPE_EXT_OR) { + /* If we aren't told an address for this connection, we should + * presume it isn't local, and should be rate-limited. */ + TO_CONN(or_conn)->always_rate_limit_as_remote = 1; connection_or_set_ext_or_identifier(or_conn); + } return or_conn; } @@ -3043,7 +3047,14 @@ retry_all_listeners(smartlist_t *new_conns, int close_all_noncontrol) &skip, &addr_in_use); } - tor_assert(new_conn); + /* There are many reasons why we can't open a new listener port so in case + * we hit those, bail early so tor can stop. */ + if (!new_conn) { + log_warn(LD_NET, "Unable to create listener port: %s:%d", + fmt_addr(&r->new_port->addr), r->new_port->port); + retval = -1; + break; + } smartlist_add(new_conns, new_conn); @@ -3145,6 +3156,7 @@ connection_is_rate_limited(const connection_t *conn) if (conn->linked) return 0; /* Internal connection */ else if (! options->CountPrivateBandwidth && + ! conn->always_rate_limit_as_remote && (tor_addr_family(&conn->addr) == AF_UNSPEC || /* no address */ tor_addr_family(&conn->addr) == AF_UNIX || /* no address */ tor_addr_is_internal(&conn->addr, 0))) diff --git a/src/core/or/channel.c b/src/core/or/channel.c index 18940bd81f..462324ccc8 100644 --- a/src/core/or/channel.c +++ b/src/core/or/channel.c @@ -72,6 +72,7 @@ #include "core/or/relay.h" #include "core/or/scheduler.h" #include "feature/client/entrynodes.h" +#include "feature/nodelist/dirlist.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerlist.h" @@ -663,7 +664,7 @@ channel_find_by_global_id(uint64_t global_identifier) /** Return true iff <b>chan</b> matches <b>rsa_id_digest</b> and <b>ed_id</b>. * as its identity keys. If either is NULL, do not check for a match. */ -static int +int channel_remote_identity_matches(const channel_t *chan, const char *rsa_id_digest, const ed25519_public_key_t *ed_id) @@ -749,6 +750,7 @@ channel_check_for_duplicates(void) { channel_idmap_entry_t **iter; channel_t *chan; + int total_dirauth_connections = 0, total_dirauths = 0; int total_relay_connections = 0, total_relays = 0, total_canonical = 0; int total_half_canonical = 0; int total_gt_one_connection = 0, total_gt_two_connections = 0; @@ -756,13 +758,18 @@ channel_check_for_duplicates(void) HT_FOREACH(iter, channel_idmap, &channel_identity_map) { int connections_to_relay = 0; + const char *id_digest = (char *) (*iter)->digest; /* Only consider relay connections */ - if (!connection_or_digest_is_known_relay((char*)(*iter)->digest)) + if (!connection_or_digest_is_known_relay(id_digest)) continue; total_relays++; + const bool is_dirauth = router_digest_is_trusted_dir(id_digest); + if (is_dirauth) + total_dirauths++; + for (chan = TOR_LIST_FIRST(&(*iter)->channel_list); chan; chan = channel_next_with_rsa_identity(chan)) { @@ -771,11 +778,12 @@ channel_check_for_duplicates(void) connections_to_relay++; total_relay_connections++; + if (is_dirauth) + total_dirauth_connections++; - if (chan->is_canonical(chan, 0)) total_canonical++; + if (chan->is_canonical(chan)) total_canonical++; - if (!chan->is_canonical_to_peer && chan->is_canonical(chan, 0) - && chan->is_canonical(chan, 1)) { + if (!chan->is_canonical_to_peer && chan->is_canonical(chan)) { total_half_canonical++; } } @@ -785,11 +793,28 @@ channel_check_for_duplicates(void) if (connections_to_relay > 4) total_gt_four_connections++; } -#define MIN_RELAY_CONNECTIONS_TO_WARN 5 + /* Don't bother warning about excessive connections unless we have + * at least this many connections, total. + */ +#define MIN_RELAY_CONNECTIONS_TO_WARN 25 + /* If the average number of connections for a regular relay is more than + * this, that's too high. + */ +#define MAX_AVG_RELAY_CONNECTIONS 1.5 + /* If the average number of connections for a dirauth is more than + * this, that's too high. + */ +#define MAX_AVG_DIRAUTH_CONNECTIONS 4 + + /* How many connections total would be okay, given the number of + * relays and dirauths that we have connections to? */ + const int max_tolerable_connections = (int)( + (total_relays-total_dirauths) * MAX_AVG_RELAY_CONNECTIONS + + total_dirauths * MAX_AVG_DIRAUTH_CONNECTIONS); /* If we average 1.5 or more connections per relay, something is wrong */ if (total_relays > MIN_RELAY_CONNECTIONS_TO_WARN && - total_relay_connections >= 1.5*total_relays) { + total_relay_connections > max_tolerable_connections) { log_notice(LD_OR, "Your relay has a very large number of connections to other relays. " "Is your outbound address the same as your relay address? " @@ -2419,21 +2444,9 @@ channel_get_for_extend(const char *rsa_id_digest, continue; } - /* Never return a non-canonical connection using a recent link protocol - * if the address is not what we wanted. - * - * The channel_is_canonical_is_reliable() function asks the lower layer - * if we should trust channel_is_canonical(). The below is from the - * comments of the old circuit_or_get_for_extend() and applies when - * the lower-layer transport is channel_tls_t. - * - * (For old link protocols, we can't rely on is_canonical getting - * set properly if we're talking to the right address, since we might - * have an out-of-date descriptor, and we will get no NETINFO cell to - * tell us about the right address.) - */ + /* Only return canonical connections or connections where the address + * is the address we wanted. */ if (!channel_is_canonical(chan) && - channel_is_canonical_is_reliable(chan) && !channel_matches_target_addr_for_extend(chan, target_addr)) { ++n_noncanonical; continue; @@ -2575,16 +2588,12 @@ channel_dump_statistics, (channel_t *chan, int severity)) /* Handle marks */ tor_log(severity, LD_GENERAL, - " * Channel %"PRIu64 " has these marks: %s %s %s " - "%s %s %s", + " * Channel %"PRIu64 " has these marks: %s %s %s %s %s", (chan->global_identifier), channel_is_bad_for_new_circs(chan) ? "bad_for_new_circs" : "!bad_for_new_circs", channel_is_canonical(chan) ? "canonical" : "!canonical", - channel_is_canonical_is_reliable(chan) ? - "is_canonical_is_reliable" : - "!is_canonical_is_reliable", channel_is_client(chan) ? "client" : "!client", channel_is_local(chan) ? @@ -2943,22 +2952,7 @@ channel_is_canonical(channel_t *chan) tor_assert(chan); tor_assert(chan->is_canonical); - return chan->is_canonical(chan, 0); -} - -/** - * Test if the canonical flag is reliable. - * - * This function asks if the lower layer thinks it's safe to trust the - * result of channel_is_canonical(). - */ -int -channel_is_canonical_is_reliable(channel_t *chan) -{ - tor_assert(chan); - tor_assert(chan->is_canonical); - - return chan->is_canonical(chan, 1); + return chan->is_canonical(chan); } /** diff --git a/src/core/or/channel.h b/src/core/or/channel.h index 2e2936a69a..becb0c973a 100644 --- a/src/core/or/channel.h +++ b/src/core/or/channel.h @@ -350,12 +350,10 @@ struct channel_t { /** Check if the lower layer has queued writes */ int (*has_queued_writes)(channel_t *); /** - * If the second param is zero, ask the lower layer if this is - * 'canonical', for a transport-specific definition of canonical; if - * it is 1, ask if the answer to the preceding query is safe to rely - * on. + * Ask the lower layer if this is 'canonical', for a transport-specific + * definition of canonical. */ - int (*is_canonical)(channel_t *, int); + int (*is_canonical)(channel_t *); /** Check if this channel matches a specified extend_info_t */ int (*matches_extend_info)(channel_t *, extend_info_t *); /** Check if this channel matches a target address when extending */ @@ -728,7 +726,6 @@ int channel_has_queued_writes(channel_t *chan); int channel_is_bad_for_new_circs(channel_t *chan); void channel_mark_bad_for_new_circs(channel_t *chan); int channel_is_canonical(channel_t *chan); -int channel_is_canonical_is_reliable(channel_t *chan); int channel_is_client(const channel_t *chan); int channel_is_local(channel_t *chan); int channel_is_incoming(channel_t *chan); @@ -736,6 +733,9 @@ int channel_is_outgoing(channel_t *chan); void channel_mark_client(channel_t *chan); void channel_clear_client(channel_t *chan); int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info); +int channel_remote_identity_matches(const channel_t *chan, + const char *rsa_id_digest, + const ed25519_public_key_t *ed_id); int channel_matches_target_addr_for_extend(channel_t *chan, const tor_addr_t *target); unsigned int channel_num_circuits(channel_t *chan); diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c index efa8e2d891..8a087298de 100644 --- a/src/core/or/channeltls.c +++ b/src/core/or/channeltls.c @@ -107,7 +107,7 @@ channel_tls_get_transport_name_method(channel_t *chan, char **transport_out); static const char * channel_tls_get_remote_descr_method(channel_t *chan, int flags); static int channel_tls_has_queued_writes_method(channel_t *chan); -static int channel_tls_is_canonical_method(channel_t *chan, int req); +static int channel_tls_is_canonical_method(channel_t *chan); static int channel_tls_matches_extend_info_method(channel_t *chan, extend_info_t *extend_info); @@ -645,12 +645,11 @@ channel_tls_has_queued_writes_method(channel_t *chan) /** * Tell the upper layer if we're canonical. * - * This implements the is_canonical method for channel_tls_t; if req is zero, - * it returns whether this is a canonical channel, and if it is one it returns - * whether that can be relied upon. + * This implements the is_canonical method for channel_tls_t: + * it returns whether this is a canonical channel. */ static int -channel_tls_is_canonical_method(channel_t *chan, int req) +channel_tls_is_canonical_method(channel_t *chan) { int answer = 0; channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); @@ -658,24 +657,13 @@ channel_tls_is_canonical_method(channel_t *chan, int req) tor_assert(tlschan); if (tlschan->conn) { - switch (req) { - case 0: - answer = tlschan->conn->is_canonical; - break; - case 1: - /* - * Is the is_canonical bit reliable? In protocols version 2 and up - * we get the canonical address from a NETINFO cell, but in older - * versions it might be based on an obsolete descriptor. - */ - answer = (tlschan->conn->link_proto >= 2); - break; - default: - /* This shouldn't happen; channel.c is broken if it does */ - tor_assert_nonfatal_unreached_once(); - } + /* If this bit is set to 0, and link_proto is sufficiently old, then we + * can't actually _rely_ on this being a non-canonical channel. + * Nonetheless, we're going to believe that this is a non-canonical + * channel in this case, since nobody should be using these link protocols + * any more. */ + answer = tlschan->conn->is_canonical; } - /* else return 0 for tlschan->conn == NULL */ return answer; } @@ -1237,8 +1225,7 @@ channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn) * the v2 and v3 handshakes. */ /* But that should be happening any longer've disabled bufferevents. */ tor_assert_nonfatal_unreached_once(); - - FALLTHROUGH; + FALLTHROUGH_UNLESS_ALL_BUGS_ARE_FATAL; case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: if (!(command_allowed_before_handshake(var_cell->command))) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c index 4865d05d98..fa25e6b672 100644 --- a/src/core/or/circuitbuild.c +++ b/src/core/or/circuitbuild.c @@ -646,21 +646,37 @@ circuit_n_chan_done(channel_t *chan, int status, int close_origin_circuits) circ->state != CIRCUIT_STATE_CHAN_WAIT) continue; - if (tor_digest_is_zero(circ->n_hop->identity_digest)) { + const char *rsa_ident = NULL; + const ed25519_public_key_t *ed_ident = NULL; + if (! tor_digest_is_zero(circ->n_hop->identity_digest)) { + rsa_ident = circ->n_hop->identity_digest; + } + if (! ed25519_public_key_is_zero(&circ->n_hop->ed_identity)) { + ed_ident = &circ->n_hop->ed_identity; + } + + if (rsa_ident == NULL && ed_ident == NULL) { /* Look at addr/port. This is an unkeyed connection. */ if (!channel_matches_extend_info(chan, circ->n_hop)) continue; } else { - /* We expected a key. See if it's the right one. */ - if (tor_memneq(chan->identity_digest, - circ->n_hop->identity_digest, DIGEST_LEN)) + /* We expected a key or keys. See if they matched. */ + if (!channel_remote_identity_matches(chan, rsa_ident, ed_ident)) + continue; + + /* If the channel is canonical, great. If not, it needs to match + * the requested address exactly. */ + if (! chan->is_canonical && + ! channel_matches_extend_info(chan, circ->n_hop)) { continue; + } } if (!status) { /* chan failed; close circ */ log_info(LD_CIRC,"Channel failed; closing circ."); circuit_mark_for_close(circ, END_CIRC_REASON_CHANNEL_CLOSED); continue; } + if (close_origin_circuits && CIRCUIT_IS_ORIGIN(circ)) { log_info(LD_CIRC,"Channel deprecated for origin circs; closing circ."); circuit_mark_for_close(circ, END_CIRC_REASON_CHANNEL_CLOSED); @@ -730,6 +746,8 @@ circuit_deliver_create_cell(circuit_t *circ, const create_cell_t *create_cell, goto error; } + tor_assert_nonfatal_once(circ->n_chan->is_canonical); + memset(&cell, 0, sizeof(cell_t)); r = relayed ? create_cell_format_relayed(&cell, create_cell) : create_cell_format(&cell, create_cell); diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c index 96cc718029..6f07132e22 100644 --- a/src/core/or/circuitlist.c +++ b/src/core/or/circuitlist.c @@ -846,7 +846,7 @@ circuit_purpose_to_controller_hs_state_string(uint8_t purpose) "Unrecognized circuit purpose: %d", (int)purpose); tor_fragile_assert(); - FALLTHROUGH; + FALLTHROUGH_UNLESS_ALL_BUGS_ARE_FATAL; case CIRCUIT_PURPOSE_OR: case CIRCUIT_PURPOSE_C_GENERAL: diff --git a/src/core/or/connection_st.h b/src/core/or/connection_st.h index 55d94d9451..685c9f89f4 100644 --- a/src/core/or/connection_st.h +++ b/src/core/or/connection_st.h @@ -69,6 +69,9 @@ struct connection_t { /** True if connection_handle_write is currently running on this connection. */ unsigned int in_connection_handle_write:1; + /** If true, then we treat this connection as remote for the purpose of + * rate-limiting, no matter what its address is. */ + unsigned int always_rate_limit_as_remote:1; /* For linked connections: */ diff --git a/src/core/or/protover.c b/src/core/or/protover.c index 2a0a06f951..7abb59cf31 100644 --- a/src/core/or/protover.c +++ b/src/core/or/protover.c @@ -118,13 +118,13 @@ proto_entry_free_(proto_entry_t *entry) } /** The largest possible protocol version. */ -#define MAX_PROTOCOL_VERSION (UINT32_MAX-1) +#define MAX_PROTOCOL_VERSION (63) /** * Given a string <b>s</b> and optional end-of-string pointer * <b>end_of_range</b>, parse the protocol range and store it in * <b>low_out</b> and <b>high_out</b>. A protocol range has the format U, or - * U-U, where U is an unsigned 32-bit integer. + * U-U, where U is an unsigned integer between 0 and 63 inclusive. */ static int parse_version_range(const char *s, const char *end_of_range, diff --git a/src/feature/client/entrynodes.c b/src/feature/client/entrynodes.c index 148e6471ba..c4d3d17c62 100644 --- a/src/feature/client/entrynodes.c +++ b/src/feature/client/entrynodes.c @@ -2263,7 +2263,7 @@ entry_guards_note_guard_success(guard_selection_t *gs, break; default: tor_assert_nonfatal_unreached(); - FALLTHROUGH; + FALLTHROUGH_UNLESS_ALL_BUGS_ARE_FATAL; case GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD: if (guard->is_primary) { /* XXXX #20832 -- I don't actually like this logic. It seems to make diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c index 8259c3b353..b3f61549ad 100644 --- a/src/feature/control/control_cmd.c +++ b/src/feature/control/control_cmd.c @@ -1430,7 +1430,7 @@ handle_control_hsfetch(control_connection_t *conn, rend_valid_descriptor_id(arg1 + v2_str_len) && base32_decode(digest, sizeof(digest), arg1 + v2_str_len, REND_DESC_ID_V2_LEN_BASE32) == - REND_DESC_ID_V2_LEN_BASE32) { + sizeof(digest)) { /* We have a well formed version 2 descriptor ID. Keep the decoded value * of the id. */ desc_id = digest; diff --git a/src/feature/relay/ext_orport.c b/src/feature/relay/ext_orport.c index ce4e043dd7..3a9e4abfd0 100644 --- a/src/feature/relay/ext_orport.c +++ b/src/feature/relay/ext_orport.c @@ -494,6 +494,10 @@ connection_ext_or_handle_cmd_useraddr(connection_t *conn, } conn->address = tor_addr_to_str_dup(&addr); + /* Now that we know the address, we don't have to manually override rate + * limiting. */ + conn->always_rate_limit_as_remote = 0; + return 0; } diff --git a/src/feature/rend/rendclient.c b/src/feature/rend/rendclient.c index d33b61851f..427491e3a8 100644 --- a/src/feature/rend/rendclient.c +++ b/src/feature/rend/rendclient.c @@ -831,7 +831,7 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro, log_warn(LD_BUG, "Unknown failure type %u. Removing intro point.", failure_type); tor_fragile_assert(); - FALLTHROUGH; + FALLTHROUGH_UNLESS_ALL_BUGS_ARE_FATAL; case INTRO_POINT_FAILURE_GENERIC: rend_cache_intro_failure_note(failure_type, (uint8_t *)failed_intro->identity_digest, diff --git a/src/feature/rend/rendservice.c b/src/feature/rend/rendservice.c index 182e935fa1..12ff056678 100644 --- a/src/feature/rend/rendservice.c +++ b/src/feature/rend/rendservice.c @@ -131,6 +131,22 @@ static smartlist_t *rend_service_list = NULL; * service on config reload. */ static smartlist_t *rend_service_staging_list = NULL; +/** Helper: log the deprecation warning for version 2 only once. */ +static void +log_once_deprecation_warning(void) +{ + static bool logged_once = false; + if (!logged_once) { + log_warn(LD_REND, "DEPRECATED: Onion service version 2 are deprecated. " + "Please use version 3 which is the default now. " + "Currently, version 2 is planned to be obsolete in " + "the Tor version 0.4.6 stable series."); + logged_once = true; + } +} +/** Macro to make it very explicit that we are warning about deprecation. */ +#define WARN_ONCE_DEPRECATION() log_once_deprecation_warning() + /* Like rend_get_service_list_mutable, but returns a read-only list. */ static const smartlist_t* rend_get_service_list(const smartlist_t* substitute_service_list) @@ -732,6 +748,9 @@ rend_config_service(const config_line_t *line_, tor_assert(options); tor_assert(config); + /* We are about to configure a version 2 service. Warn of deprecation. */ + WARN_ONCE_DEPRECATION(); + /* Use the staging service list so that we can check then do the pruning * process using the main list at the end. */ if (rend_service_staging_list == NULL) { diff --git a/src/lib/log/util_bug.h b/src/lib/log/util_bug.h index ae3d125a08..d9dbc7ee09 100644 --- a/src/lib/log/util_bug.h +++ b/src/lib/log/util_bug.h @@ -240,6 +240,17 @@ IF_BUG_ONCE__(ASSERT_PREDICT_UNLIKELY_(cond), \ IF_BUG_ONCE_VARNAME__(__LINE__)) +/** + * Use this macro after a nonfatal assertion, and before a case statement + * where you would want to fall through. + */ +#ifdef ALL_BUGS_ARE_FATAL +#define FALLTHROUGH_UNLESS_ALL_BUGS_ARE_FATAL \ + abort() +#else +#define FALLTHROUGH_UNLESS_ALL_BUGS_ARE_FATAL FALLTHROUGH +#endif + /** Define this if you want Tor to crash when any problem comes up, * so you can get a coredump and track things down. */ // #define tor_fragile_assert() tor_assert_unreached(0) diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c index 38c7efe107..559df36d7b 100644 --- a/src/lib/tls/tortls_nss.c +++ b/src/lib/tls/tortls_nss.c @@ -418,6 +418,16 @@ tor_tls_new(tor_socket_t sock, int is_server) return NULL; } + /* even if though the socket is already nonblocking, we need to tell NSS + * about the fact, so that it knows what to do when it says EAGAIN. */ + PRSocketOptionData data; + data.option = PR_SockOpt_Nonblocking; + data.value.non_blocking = 1; + if (PR_SetSocketOption(ssl, &data) != PR_SUCCESS) { + PR_Close(ssl); + return NULL; + } + tor_tls_t *tls = tor_malloc_zero(sizeof(tor_tls_t)); tls->magic = TOR_TLS_MAGIC; tls->context = ctx; diff --git a/src/rust/protover/errors.rs b/src/rust/protover/errors.rs index dc0d8735f4..04397ac4fe 100644 --- a/src/rust/protover/errors.rs +++ b/src/rust/protover/errors.rs @@ -36,7 +36,7 @@ impl Display for ProtoverError { ProtoverError::Unparseable => write!(f, "The protover string was unparseable."), ProtoverError::ExceedsMax => write!( f, - "The high in a (low, high) protover range exceeds u32::MAX." + "The high in a (low, high) protover range exceeds 63." ), ProtoverError::ExceedsExpansionLimit => write!( f, diff --git a/src/rust/protover/protoset.rs b/src/rust/protover/protoset.rs index 3b283983c8..0ab94457c5 100644 --- a/src/rust/protover/protoset.rs +++ b/src/rust/protover/protoset.rs @@ -294,6 +294,10 @@ impl ProtoSet { } } +/// Largest allowed protocol version. +/// C_RUST_COUPLED: protover.c `MAX_PROTOCOL_VERSION` +const MAX_PROTOCOL_VERSION: Version = 63; + impl FromStr for ProtoSet { type Err = ProtoverError; @@ -370,7 +374,7 @@ impl FromStr for ProtoSet { let pieces: ::std::str::Split<char> = version_string.split(','); for p in pieces { - if p.contains('-') { + let (lo,hi) = if p.contains('-') { let mut pair = p.splitn(2, '-'); let low = pair.next().ok_or(ProtoverError::Unparseable)?; @@ -379,12 +383,17 @@ impl FromStr for ProtoSet { let lo: Version = low.parse().or(Err(ProtoverError::Unparseable))?; let hi: Version = high.parse().or(Err(ProtoverError::Unparseable))?; - pairs.push((lo, hi)); + (lo,hi) } else { let v: u32 = p.parse().or(Err(ProtoverError::Unparseable))?; - pairs.push((v, v)); + (v, v) + }; + + if lo > MAX_PROTOCOL_VERSION || hi > MAX_PROTOCOL_VERSION { + return Err(ProtoverError::ExceedsMax); } + pairs.push((lo, hi)); } ProtoSet::from_slice(&pairs[..]) @@ -674,12 +683,11 @@ mod test { #[test] fn test_protoset_into_vec() { - let ps: ProtoSet = "1-13,42,9001,4294967294".parse().unwrap(); + let ps: ProtoSet = "1-13,42".parse().unwrap(); let v: Vec<Version> = ps.into(); assert!(v.contains(&7)); - assert!(v.contains(&9001)); - assert!(v.contains(&4294967294)); + assert!(v.contains(&42)); } } diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs index 0ca960bd69..f9c6f8e50d 100644 --- a/src/rust/protover/protover.rs +++ b/src/rust/protover/protover.rs @@ -874,12 +874,12 @@ mod test { #[test] fn test_protoentry_from_str_allowed_number_of_versions() { - assert_protoentry_is_parseable!("Desc=1-4294967294"); + assert_protoentry_is_parseable!("Desc=1-63"); } #[test] fn test_protoentry_from_str_too_many_versions() { - assert_protoentry_is_unparseable!("Desc=1-4294967295"); + assert_protoentry_is_unparseable!("Desc=1-64"); } #[test] @@ -918,10 +918,10 @@ mod test { #[test] fn test_protoentry_all_supported_unsupported_high_version() { - let protocols: UnvalidatedProtoEntry = "HSDir=12-100".parse().unwrap(); + let protocols: UnvalidatedProtoEntry = "HSDir=12-60".parse().unwrap(); let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported(); assert_eq!(true, unsupported.is_some()); - assert_eq!("HSDir=12-100", &unsupported.unwrap().to_string()); + assert_eq!("HSDir=12-60", &unsupported.unwrap().to_string()); } #[test] @@ -970,7 +970,7 @@ mod test { ProtoSet::from_str(&versions).unwrap().to_string() ); - versions = "1-3,500"; + versions = "1-3,50"; assert_eq!( String::from(versions), ProtoSet::from_str(&versions).unwrap().to_string() diff --git a/src/rust/protover/tests/protover.rs b/src/rust/protover/tests/protover.rs index 942fe3c6ab..d563202d87 100644 --- a/src/rust/protover/tests/protover.rs +++ b/src/rust/protover/tests/protover.rs @@ -98,10 +98,10 @@ fn protocol_all_supported_with_unsupported_protocol() { #[test] fn protocol_all_supported_with_unsupported_versions() { - let protocols: UnvalidatedProtoEntry = "Link=3-999".parse().unwrap(); + let protocols: UnvalidatedProtoEntry = "Link=3-63".parse().unwrap(); let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported(); assert_eq!(true, unsupported.is_some()); - assert_eq!("Link=6-999", &unsupported.unwrap().to_string()); + assert_eq!("Link=6-63", &unsupported.unwrap().to_string()); } #[test] @@ -114,10 +114,10 @@ fn protocol_all_supported_with_unsupported_low_version() { #[test] fn protocol_all_supported_with_unsupported_high_version() { - let protocols: UnvalidatedProtoEntry = "Cons=1-2,999".parse().unwrap(); + let protocols: UnvalidatedProtoEntry = "Cons=1-2,60".parse().unwrap(); let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported(); assert_eq!(true, unsupported.is_some()); - assert_eq!("Cons=999", &unsupported.unwrap().to_string()); + assert_eq!("Cons=60", &unsupported.unwrap().to_string()); } #[test] @@ -195,27 +195,27 @@ fn protover_compute_vote_returns_protocols_that_it_doesnt_currently_support() { #[test] fn protover_compute_vote_returns_matching_for_mix() { - let protocols: &[UnvalidatedProtoEntry] = &["Link=1-10,500 Cons=1,3-7,8".parse().unwrap()]; + let protocols: &[UnvalidatedProtoEntry] = &["Link=1-10,50 Cons=1,3-7,8".parse().unwrap()]; let listed = ProtoverVote::compute(protocols, &1); - assert_eq!("Cons=1,3-8 Link=1-10,500", listed.to_string()); + assert_eq!("Cons=1,3-8 Link=1-10,50", listed.to_string()); } #[test] fn protover_compute_vote_returns_matching_for_longer_mix() { let protocols: &[UnvalidatedProtoEntry] = &[ - "Desc=1-10,500 Cons=1,3-7,8".parse().unwrap(), - "Link=123-456,78 Cons=2-6,8 Desc=9".parse().unwrap(), + "Desc=1-10,50 Cons=1,3-7,8".parse().unwrap(), + "Link=12-45,8 Cons=2-6,8 Desc=9".parse().unwrap(), ]; let listed = ProtoverVote::compute(protocols, &1); - assert_eq!("Cons=1-8 Desc=1-10,500 Link=78,123-456", listed.to_string()); + assert_eq!("Cons=1-8 Desc=1-10,50 Link=8,12-45", listed.to_string()); } #[test] fn protover_compute_vote_returns_matching_for_longer_mix_with_threshold_two() { let protocols: &[UnvalidatedProtoEntry] = &[ - "Desc=1-10,500 Cons=1,3-7,8".parse().unwrap(), - "Link=123-456,78 Cons=2-6,8 Desc=9".parse().unwrap(), + "Desc=1-10,50 Cons=1,3-7,8".parse().unwrap(), + "Link=8,12-45 Cons=2-6,8 Desc=9".parse().unwrap(), ]; let listed = ProtoverVote::compute(protocols, &2); @@ -320,30 +320,20 @@ fn protocol_all_supported_with_single_protocol_and_protocol_range() { assert_eq!(true, unsupported.is_none()); } -// By allowing us to add to votes, the C implementation allows us to -// exceed the limit. -#[test] -fn protover_compute_vote_may_exceed_limit() { - let proto1: UnvalidatedProtoEntry = "Sleen=1-65535".parse().unwrap(); - let proto2: UnvalidatedProtoEntry = "Sleen=100000".parse().unwrap(); - - let _result: UnvalidatedProtoEntry = ProtoverVote::compute(&[proto1, proto2], &1); -} - #[test] fn protover_all_supported_should_exclude_versions_we_actually_do_support() { - let proto: UnvalidatedProtoEntry = "Link=3-999".parse().unwrap(); + let proto: UnvalidatedProtoEntry = "Link=3-63".parse().unwrap(); let result: String = proto.all_supported().unwrap().to_string(); - assert_eq!(result, "Link=6-999".to_string()); + assert_eq!(result, "Link=6-63".to_string()); } #[test] fn protover_all_supported_should_exclude_versions_we_actually_do_support_complex1() { - let proto: UnvalidatedProtoEntry = "Link=1-3,345-666".parse().unwrap(); + let proto: UnvalidatedProtoEntry = "Link=1-3,30-63".parse().unwrap(); let result: String = proto.all_supported().unwrap().to_string(); - assert_eq!(result, "Link=345-666".to_string()); + assert_eq!(result, "Link=30-63".to_string()); } #[test] @@ -356,26 +346,10 @@ fn protover_all_supported_should_exclude_versions_we_actually_do_support_complex #[test] fn protover_all_supported_should_exclude_some_versions_and_entire_protocols() { - let proto: UnvalidatedProtoEntry = "Link=1-3,5-12 Quokka=9000-9001".parse().unwrap(); - let result: String = proto.all_supported().unwrap().to_string(); - - assert_eq!(result, "Link=6-12 Quokka=9000-9001".to_string()); -} - -#[test] -fn protover_all_supported_should_not_dos_anyones_computer() { - let proto: UnvalidatedProtoEntry = "Link=1-2147483648".parse().unwrap(); - let result: String = proto.all_supported().unwrap().to_string(); - - assert_eq!(result, "Link=6-2147483648".to_string()); -} - -#[test] -fn protover_all_supported_should_not_dos_anyones_computer_max_versions() { - let proto: UnvalidatedProtoEntry = "Link=1-4294967294".parse().unwrap(); + let proto: UnvalidatedProtoEntry = "Link=1-3,5-12 Quokka=50-51".parse().unwrap(); let result: String = proto.all_supported().unwrap().to_string(); - assert_eq!(result, "Link=6-4294967294".to_string()); + assert_eq!(result, "Link=6-12 Quokka=50-51".to_string()); } #[test] diff --git a/src/test/hs_ntor_ref.py b/src/test/hs_ntor_ref.py index f107cc36ca..98025dd584 100644 --- a/src/test/hs_ntor_ref.py +++ b/src/test/hs_ntor_ref.py @@ -70,14 +70,16 @@ except ImportError: try: # Pull the sha3 functions in. from hashlib import sha3_256, shake_256 - shake_squeeze = shake_256.digest + def shake_squeeze(obj, n): + return obj.digest(n) except ImportError: if hasattr(sha3, "SHA3256"): # If this happens, then we have the old "sha3" module which # hashlib and pysha3 superseded. sha3_256 = sha3.SHA3256 shake_256 = sha3.SHAKE256 - shake_squeeze = shake_256.squeeze + def shake_squeeze(obj, n): + return obj.squeeze(n) else: # error code 77 tells automake to skip this test sys.exit(77) diff --git a/src/test/include.am b/src/test/include.am index 90e50752ce..33556083df 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -25,7 +25,16 @@ TESTSCRIPTS = \ src/test/test_workqueue_socketpair.sh \ src/test/test_switch_id.sh \ src/test/test_cmdline.sh \ - src/test/test_parseconf.sh + src/test/test_parseconf.sh \ + src/test/test_switch_id.sh \ + src/test/unittest_part1.sh \ + src/test/unittest_part2.sh \ + src/test/unittest_part3.sh \ + src/test/unittest_part4.sh \ + src/test/unittest_part5.sh \ + src/test/unittest_part6.sh \ + src/test/unittest_part7.sh \ + src/test/unittest_part8.sh if USE_RUST TESTSCRIPTS += \ @@ -53,7 +62,7 @@ TESTSCRIPTS += \ scripts/maint/checkSpaceTest.sh endif -TESTS += src/test/test src/test/test-slow src/test/test-memwipe \ +TESTS += src/test/test-slow src/test/test-memwipe \ src/test/test_workqueue \ src/test/test_keygen.sh \ src/test/test_key_expiration.sh \ @@ -432,7 +441,15 @@ EXTRA_DIST += \ src/test/test_workqueue_pipe2.sh \ src/test/test_workqueue_socketpair.sh \ src/test/test_cmdline.sh \ - src/test/test_parseconf.sh + src/test/test_parseconf.sh \ + src/test/unittest_part1.sh \ + src/test/unittest_part2.sh \ + src/test/unittest_part3.sh \ + src/test/unittest_part4.sh \ + src/test/unittest_part5.sh \ + src/test/unittest_part6.sh \ + src/test/unittest_part7.sh \ + src/test/unittest_part8.sh test-rust: $(TESTS_ENVIRONMENT) "$(abs_top_srcdir)/src/test/test_rust.sh" diff --git a/src/test/test_channel.c b/src/test/test_channel.c index 5b13f1f979..01aa01b4db 100644 --- a/src/test/test_channel.c +++ b/src/test/test_channel.c @@ -44,7 +44,6 @@ static int dump_statistics_mock_matches = 0; static int test_close_called = 0; static int test_chan_should_be_canonical = 0; static int test_chan_should_match_target = 0; -static int test_chan_canonical_should_be_reliable = 0; static int test_chan_listener_close_fn_called = 0; static int test_chan_listener_fn_called = 0; @@ -337,14 +336,10 @@ scheduler_release_channel_mock(channel_t *ch) } static int -test_chan_is_canonical(channel_t *chan, int req) +test_chan_is_canonical(channel_t *chan) { tor_assert(chan); - if (req && test_chan_canonical_should_be_reliable) { - return 1; - } - if (test_chan_should_be_canonical) { return 1; } @@ -1357,6 +1352,9 @@ test_channel_for_extend(void *arg) /* Make it older than chan1. */ chan2->timestamp_created = chan1->timestamp_created - 1; + /* Say it's all canonical. */ + test_chan_should_be_canonical = 1; + /* Set channel identities and add it to the channel map. The last one to be * added is made the first one in the list so the lookup will always return * that one first. */ @@ -1451,8 +1449,8 @@ test_channel_for_extend(void *arg) chan2->is_bad_for_new_circs = 0; /* Non canonical channels. */ + test_chan_should_be_canonical = 0; test_chan_should_match_target = 0; - test_chan_canonical_should_be_reliable = 1; ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); tt_assert(!ret_chan); tt_str_op(msg, OP_EQ, "Connections all too old, or too non-canonical. " diff --git a/src/test/test_config.c b/src/test/test_config.c index ae3f04de11..8ea744d4fd 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -703,11 +703,13 @@ test_config_parse_tcp_proxy_line(void *arg) tor_free(msg); /* Bad TCPProxy line - unparsable address/port. */ - ret = parse_tcp_proxy_line("haproxy 95.216.163.36/443", options, &msg); + MOCK(tor_addr_lookup, mock_tor_addr_lookup__fail_on_bad_addrs); + ret = parse_tcp_proxy_line("haproxy bogus_address!/300", options, &msg); tt_int_op(ret, OP_EQ, -1); tt_str_op(msg, OP_EQ, "TCPProxy address/port failed to parse or resolve. " "Please fix."); tor_free(msg); + UNMOCK(tor_addr_lookup); /* Good TCPProxy line - ipv4. */ ret = parse_tcp_proxy_line("haproxy 95.216.163.36:443", options, &msg); @@ -720,7 +722,7 @@ test_config_parse_tcp_proxy_line(void *arg) tor_free(msg); done: - ; + UNMOCK(tor_addr_lookup); } /** diff --git a/src/test/test_protover.c b/src/test/test_protover.c index f1d1ef0d4a..7341631ccc 100644 --- a/src/test/test_protover.c +++ b/src/test/test_protover.c @@ -25,7 +25,7 @@ test_protover_parse(void *arg) #else /* !defined(HAVE_RUST) */ char *re_encoded = NULL; - const char *orig = "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,900"; + const char *orig = "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16"; smartlist_t *elts = parse_protocol_list(orig); tt_assert(elts); @@ -61,7 +61,7 @@ test_protover_parse(void *arg) e = smartlist_get(elts, 3); tt_str_op(e->name, OP_EQ, "Quux"); - tt_int_op(smartlist_len(e->ranges), OP_EQ, 4); + tt_int_op(smartlist_len(e->ranges), OP_EQ, 3); { r = smartlist_get(e->ranges, 0); tt_int_op(r->low, OP_EQ, 9); @@ -74,10 +74,6 @@ test_protover_parse(void *arg) r = smartlist_get(e->ranges, 2); tt_int_op(r->low, OP_EQ, 15); tt_int_op(r->high, OP_EQ, 16); - - r = smartlist_get(e->ranges, 3); - tt_int_op(r->low, OP_EQ, 900); - tt_int_op(r->high, OP_EQ, 900); } re_encoded = encode_protocol_list(elts); @@ -149,14 +145,14 @@ test_protover_vote(void *arg) tt_str_op(result, OP_EQ, ""); tor_free(result); - smartlist_add(lst, (void*) "Foo=1-10,500 Bar=1,3-7,8"); + smartlist_add(lst, (void*) "Foo=1-10,63 Bar=1,3-7,8"); result = protover_compute_vote(lst, 1); - tt_str_op(result, OP_EQ, "Bar=1,3-8 Foo=1-10,500"); + tt_str_op(result, OP_EQ, "Bar=1,3-8 Foo=1-10,63"); tor_free(result); - smartlist_add(lst, (void*) "Quux=123-456,78 Bar=2-6,8 Foo=9"); + smartlist_add(lst, (void*) "Quux=12-45 Bar=2-6,8 Foo=9"); result = protover_compute_vote(lst, 1); - tt_str_op(result, OP_EQ, "Bar=1-8 Foo=1-10,500 Quux=78,123-456"); + tt_str_op(result, OP_EQ, "Bar=1-8 Foo=1-10,63 Quux=12-45"); tor_free(result); result = protover_compute_vote(lst, 2); @@ -194,45 +190,16 @@ test_protover_vote(void *arg) /* Just below the threshold: Rust */ smartlist_clear(lst); - smartlist_add(lst, (void*) "Sleen=1-500"); + smartlist_add(lst, (void*) "Sleen=1-50"); result = protover_compute_vote(lst, 1); - tt_str_op(result, OP_EQ, "Sleen=1-500"); + tt_str_op(result, OP_EQ, "Sleen=1-50"); tor_free(result); /* Just below the threshold: C */ smartlist_clear(lst); - smartlist_add(lst, (void*) "Sleen=1-65536"); - result = protover_compute_vote(lst, 1); - tt_str_op(result, OP_EQ, "Sleen=1-65536"); - tor_free(result); - - /* Large protover lists that exceed the threshold */ - - /* By adding two votes, C allows us to exceed the limit */ - smartlist_add(lst, (void*) "Sleen=1-65536"); - smartlist_add(lst, (void*) "Sleen=100000"); - result = protover_compute_vote(lst, 1); - tt_str_op(result, OP_EQ, "Sleen=1-65536,100000"); - tor_free(result); - - /* Large integers */ - smartlist_clear(lst); - smartlist_add(lst, (void*) "Sleen=4294967294"); + smartlist_add(lst, (void*) "Sleen=1-63"); result = protover_compute_vote(lst, 1); - tt_str_op(result, OP_EQ, "Sleen=4294967294"); - tor_free(result); - - /* This parses, but fails at the vote stage */ - smartlist_clear(lst); - smartlist_add(lst, (void*) "Sleen=4294967295"); - result = protover_compute_vote(lst, 1); - tt_str_op(result, OP_EQ, ""); - tor_free(result); - - smartlist_clear(lst); - smartlist_add(lst, (void*) "Sleen=4294967296"); - result = protover_compute_vote(lst, 1); - tt_str_op(result, OP_EQ, ""); + tt_str_op(result, OP_EQ, "Sleen=1-63"); tor_free(result); /* Protocol name too long */ @@ -272,8 +239,8 @@ test_protover_all_supported(void *arg) tt_assert(! protover_all_supported("Wombat=9", &msg)); tt_str_op(msg, OP_EQ, "Wombat=9"); tor_free(msg); - tt_assert(! protover_all_supported("Link=999", &msg)); - tt_str_op(msg, OP_EQ, "Link=999"); + tt_assert(! protover_all_supported("Link=60", &msg)); + tt_str_op(msg, OP_EQ, "Link=60"); tor_free(msg); // Mix of things we support and things we don't @@ -283,11 +250,11 @@ test_protover_all_supported(void *arg) /* Mix of things we support and don't support within a single protocol * which we do support */ - tt_assert(! protover_all_supported("Link=3-999", &msg)); - tt_str_op(msg, OP_EQ, "Link=6-999"); + tt_assert(! protover_all_supported("Link=3-60", &msg)); + tt_str_op(msg, OP_EQ, "Link=6-60"); tor_free(msg); - tt_assert(! protover_all_supported("Link=1-3,345-666", &msg)); - tt_str_op(msg, OP_EQ, "Link=345-666"); + tt_assert(! protover_all_supported("Link=1-3,50-63", &msg)); + tt_str_op(msg, OP_EQ, "Link=50-63"); tor_free(msg); tt_assert(! protover_all_supported("Link=1-3,5-12", &msg)); tt_str_op(msg, OP_EQ, "Link=6-12"); @@ -295,18 +262,8 @@ test_protover_all_supported(void *arg) /* Mix of protocols we do support and some we don't, where the protocols * we do support have some versions we don't support. */ - tt_assert(! protover_all_supported("Link=1-3,5-12 Quokka=9000-9001", &msg)); - tt_str_op(msg, OP_EQ, "Link=6-12 Quokka=9000-9001"); - tor_free(msg); - - /* We shouldn't be able to DoS ourselves parsing a large range. */ - tt_assert(! protover_all_supported("Sleen=1-2147483648", &msg)); - tt_str_op(msg, OP_EQ, "Sleen=1-2147483648"); - tor_free(msg); - - /* This case is allowed. */ - tt_assert(! protover_all_supported("Sleen=1-4294967294", &msg)); - tt_str_op(msg, OP_EQ, "Sleen=1-4294967294"); + tt_assert(! protover_all_supported("Link=1-3,5-12 Quokka=40-41", &msg)); + tt_str_op(msg, OP_EQ, "Link=6-12 Quokka=40-41"); tor_free(msg); /* If we get a (barely) valid (but unsupported list, we say "yes, that's @@ -566,9 +523,9 @@ test_protover_vote_roundtrip(void *args) /* Will fail because of 4294967295. */ { "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,900 Zn=1,4294967295", NULL }, - { "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,900 Zn=1,4294967294", - "Bar=3 Foo=1,3 Quux=9-12,14-16,900 Zn=1,4294967294" }, - { "Zu16=1,65536", "Zu16=1,65536" }, + { "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,50 Zn=1,42", + "Bar=3 Foo=1,3 Quux=9-12,14-16,50 Zn=1,42" }, + { "Zu16=1,63", "Zu16=1,63" }, { "N-1=1,2", "N-1=1-2" }, { "-1=4294967295", NULL }, { "-1=3", "-1=3" }, @@ -602,12 +559,8 @@ test_protover_vote_roundtrip(void *args) /* Large integers */ { "Link=4294967296", NULL }, /* Large range */ - { "Sleen=1-501", "Sleen=1-501" }, + { "Sleen=1-63", "Sleen=1-63" }, { "Sleen=1-65537", NULL }, - /* Both C/Rust implementations should be able to handle this mild DoS. */ - { "Sleen=1-2147483648", NULL }, - /* Rust tests are built in debug mode, so ints are bounds-checked. */ - { "Sleen=1-4294967295", NULL }, }; unsigned u; smartlist_t *votes = smartlist_new(); diff --git a/src/test/testing_common.c b/src/test/testing_common.c index e9aa4112c0..d7c6edb646 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -286,6 +286,8 @@ main(int c, const char **v) /* Don't add default logs; the tests manage their own. */ quiet_level = QUIET_SILENT; + unsigned num=1, den=1; + for (i_out = i = 1; i < c; ++i) { if (!strcmp(v[i], "--warn")) { loglevel = LOG_WARN; @@ -297,6 +299,19 @@ main(int c, const char **v) loglevel = LOG_DEBUG; } else if (!strcmp(v[i], "--accel")) { accel_crypto = 1; + } else if (!strcmp(v[i], "--fraction")) { + if (i+1 == c) { + printf("--fraction needs an argument.\n"); + return 1; + } + const char *fracstr = v[++i]; + char ch; + if (sscanf(fracstr, "%u/%u%c", &num, &den, &ch) != 2) { + printf("--fraction expects a fraction as an input.\n"); + } + if (den == 0 || num == 0 || num > den) { + printf("--fraction expects a valid fraction as an input.\n"); + } } else { v[i_out++] = v[i]; } @@ -373,6 +388,33 @@ main(int c, const char **v) smartlist_free(skip); } + if (den != 1) { + // count the tests. Linear but fast. + unsigned n_tests = 0; + struct testgroup_t *tg; + struct testcase_t *tc; + for (tg = testgroups; tg->prefix != NULL; ++tg) { + for (tc = tg->cases; tc->name != NULL; ++tc) { + ++n_tests; + } + } + // Which tests should we run? This can give iffy results if den is huge + // but it doesn't actually matter in practice. + unsigned tests_per_chunk = CEIL_DIV(n_tests, den); + unsigned start_at = (num-1) * tests_per_chunk; + + // Skip the tests that are outside of the range. + unsigned idx = 0; + for (tg = testgroups; tg->prefix != NULL; ++tg) { + for (tc = tg->cases; tc->name != NULL; ++tc) { + if (idx < start_at || idx >= start_at + tests_per_chunk) { + tc->flags |= TT_SKIP; + } + ++idx; + } + } + } + int have_failed = (tinytest_main(c, v, testgroups) != 0); free_pregenerated_keys(); diff --git a/src/test/unittest_part1.sh b/src/test/unittest_part1.sh new file mode 100755 index 0000000000..5be0f499f9 --- /dev/null +++ b/src/test/unittest_part1.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +"${abs_top_builddir:-.}/src/test/test" --fraction 1/8 diff --git a/src/test/unittest_part2.sh b/src/test/unittest_part2.sh new file mode 100755 index 0000000000..9a614eb8c1 --- /dev/null +++ b/src/test/unittest_part2.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +"${abs_top_builddir:-.}/src/test/test" --fraction 2/8 diff --git a/src/test/unittest_part3.sh b/src/test/unittest_part3.sh new file mode 100755 index 0000000000..5cbc3fe495 --- /dev/null +++ b/src/test/unittest_part3.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +"${abs_top_builddir:-.}/src/test/test" --fraction 3/8 diff --git a/src/test/unittest_part4.sh b/src/test/unittest_part4.sh new file mode 100755 index 0000000000..bc6fe01f68 --- /dev/null +++ b/src/test/unittest_part4.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +"${abs_top_builddir:-.}/src/test/test" --fraction 4/8 diff --git a/src/test/unittest_part5.sh b/src/test/unittest_part5.sh new file mode 100755 index 0000000000..9bbff34fb8 --- /dev/null +++ b/src/test/unittest_part5.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +"${abs_top_builddir:-.}/src/test/test" --fraction 5/8 diff --git a/src/test/unittest_part6.sh b/src/test/unittest_part6.sh new file mode 100755 index 0000000000..2d5eaa8a28 --- /dev/null +++ b/src/test/unittest_part6.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +"${abs_top_builddir:-.}/src/test/test" --fraction 6/8 diff --git a/src/test/unittest_part7.sh b/src/test/unittest_part7.sh new file mode 100755 index 0000000000..5e6ce2aea5 --- /dev/null +++ b/src/test/unittest_part7.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +"${abs_top_builddir:-.}/src/test/test" --fraction 7/8 diff --git a/src/test/unittest_part8.sh b/src/test/unittest_part8.sh new file mode 100755 index 0000000000..7fea9c9c7f --- /dev/null +++ b/src/test/unittest_part8.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +"${abs_top_builddir:-.}/src/test/test" --fraction 8/8 diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index ee203da680..cfeffe581c 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -217,7 +217,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.4.3.6-dev" +#define VERSION "0.4.3.7-dev" #define HAVE_STRUCT_SOCKADDR_IN6 #define HAVE_STRUCT_IN6_ADDR |