|
@@ -222,7 +222,7 @@ cc_consensus_has_changed(const networkstatus_t *ns)
|
|
|
|
|
|
|
|
|
* configuration. */
|
|
|
-STATIC uint32_t
|
|
|
+STATIC uint64_t
|
|
|
get_circuit_rate_per_second(void)
|
|
|
{
|
|
|
return dos_cc_circuit_rate;
|
|
@@ -234,31 +234,40 @@ get_circuit_rate_per_second(void)
|
|
|
STATIC void
|
|
|
cc_stats_refill_bucket(cc_client_stats_t *stats, const tor_addr_t *addr)
|
|
|
{
|
|
|
- uint32_t new_circuit_bucket_count, circuit_rate = 0, num_token;
|
|
|
- time_t now, elapsed_time_last_refill;
|
|
|
+ uint32_t new_circuit_bucket_count;
|
|
|
+ uint64_t num_token, elapsed_time_last_refill = 0, circuit_rate = 0;
|
|
|
+ time_t now;
|
|
|
+ int64_t last_refill_ts;
|
|
|
|
|
|
tor_assert(stats);
|
|
|
tor_assert(addr);
|
|
|
|
|
|
now = approx_time();
|
|
|
+ last_refill_ts = (int64_t)stats->last_circ_bucket_refill_ts;
|
|
|
|
|
|
-
|
|
|
- * and we are done. */
|
|
|
- if (stats->last_circ_bucket_refill_ts == 0) {
|
|
|
- num_token = dos_cc_circuit_burst;
|
|
|
- goto end;
|
|
|
+
|
|
|
+ * Note: If a relay's clock is ever 0, any new clients won't get a refill
|
|
|
+ * until the next second. But a relay that thinks it is 1970 will never
|
|
|
+ * validate the public consensus. */
|
|
|
+ if ((int64_t)now == last_refill_ts) {
|
|
|
+ goto done;
|
|
|
}
|
|
|
|
|
|
|
|
|
- * first compute the circuit rate that is how many circuit are we allowed to
|
|
|
- * do per second. */
|
|
|
+ * first get the circuit rate that is how many circuit are we allowed to do
|
|
|
+ * per second. */
|
|
|
circuit_rate = get_circuit_rate_per_second();
|
|
|
|
|
|
-
|
|
|
- elapsed_time_last_refill = now - stats->last_circ_bucket_refill_ts;
|
|
|
+
|
|
|
+ * and we are done.
|
|
|
+ * Note: If a relay's clock is ever 0, all clients that were last refilled
|
|
|
+ * in that zero second will get a full refill here. */
|
|
|
+ if (last_refill_ts == 0) {
|
|
|
+ num_token = dos_cc_circuit_burst;
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
|
|
|
-
|
|
|
- * that case, lets be safe and fill it up to the maximum. Not filling it
|
|
|
+
|
|
|
* could trigger a detection for a valid client. Also, if the clock jumped
|
|
|
* negative but we didn't notice until the elapsed time became positive
|
|
|
* again, then we potentially spent many seconds not refilling the bucket
|
|
@@ -266,28 +275,51 @@ cc_stats_refill_bucket(cc_client_stats_t *stats, const tor_addr_t *addr)
|
|
|
* until now means that no circuit creation requests came in during that
|
|
|
* time, so the client doesn't end up punished that much from this hopefully
|
|
|
* rare situation.*/
|
|
|
- if (elapsed_time_last_refill < 0) {
|
|
|
-
|
|
|
- * give us the maximum allowed value of token. */
|
|
|
- elapsed_time_last_refill = (dos_cc_circuit_burst / circuit_rate);
|
|
|
+ if ((int64_t)now < last_refill_ts) {
|
|
|
+
|
|
|
+ num_token = dos_cc_circuit_burst;
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * This subtraction can't underflow, because now >= last_refill_ts.
|
|
|
+ * And it can't overflow, because INT64_MAX - (-INT64_MIN) == UINT64_MAX. */
|
|
|
+ elapsed_time_last_refill = (uint64_t)now - last_refill_ts;
|
|
|
+
|
|
|
+
|
|
|
+ * If the multiplication would overflow, use the maximum allowed value. */
|
|
|
+ if (elapsed_time_last_refill > UINT32_MAX) {
|
|
|
+ num_token = dos_cc_circuit_burst;
|
|
|
+ goto end;
|
|
|
}
|
|
|
|
|
|
|
|
|
- * add to the bucket. This can be big but it is cap to a maximum after. */
|
|
|
+ * add to the bucket. This can't overflow, because both multiplicands
|
|
|
+ * are less than or equal to UINT32_MAX, and num_token is uint64_t. */
|
|
|
num_token = elapsed_time_last_refill * circuit_rate;
|
|
|
|
|
|
end:
|
|
|
-
|
|
|
- * over time. */
|
|
|
- new_circuit_bucket_count = MIN(stats->circuit_bucket + num_token,
|
|
|
- dos_cc_circuit_burst);
|
|
|
+
|
|
|
+ if (num_token > UINT32_MAX - stats->circuit_bucket) {
|
|
|
+ new_circuit_bucket_count = dos_cc_circuit_burst;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ * over time. */
|
|
|
+ new_circuit_bucket_count = MIN(stats->circuit_bucket + (uint32_t)num_token,
|
|
|
+ dos_cc_circuit_burst);
|
|
|
+ }
|
|
|
+
|
|
|
+ tor_assert_nonfatal(new_circuit_bucket_count >= stats->circuit_bucket);
|
|
|
log_debug(LD_DOS, "DoS address %s has its circuit bucket value: %" PRIu32
|
|
|
- ". Filling it to %" PRIu32 ". Circuit rate is %" PRIu32,
|
|
|
+ ". Filling it to %" PRIu32 ". Circuit rate is %" PRIu64
|
|
|
+ ". Elapsed time is %" PRIi64,
|
|
|
fmt_addr(addr), stats->circuit_bucket, new_circuit_bucket_count,
|
|
|
- circuit_rate);
|
|
|
+ circuit_rate, (int64_t)elapsed_time_last_refill);
|
|
|
|
|
|
stats->circuit_bucket = new_circuit_bucket_count;
|
|
|
stats->last_circ_bucket_refill_ts = now;
|
|
|
+
|
|
|
+ done:
|
|
|
return;
|
|
|
}
|
|
|
|