Memory quota tracking in Arti, for Onion Service DoS resistance

by Diziet | November 19, 2024

Last week we released Arti 1.3.0, the latest version of our rewrite of Tor in Rust. One new feature in this release is memory quota tracking.

Tracking and restricting memory for queued data

The memory quota tracking feature allows you to restrict the amount of memory used by an Arti process. In particular, it allows you to limit the amount of memory that other people can cause your Arti to use.

This is particularly important when Arti is being used to provide an Onion Service (aka a Tor Hidden Service). Running an Onion Service means letting users from all over the network connect to your service (depending, to an extent, on your configuration settings). That means those users can cause your system to do work, and, generally, to store data in transit to and from your Onion Service. In 2014, Jansen et al discovered that this kind of data storage can even be used to help deanonymise your service.

We have now implemented the recommended countermeasure: Arti can track how much data is stored in its various queues. When the configured limit is reached, Arti starts shutting down connections, and discarding data, until the queued data is below the limit. We kill the connections with the oldest oustanding data. This minimises the impact on unrelated, innocent, traffic.

We'll also need this memory limit feature for Arti Relay, which is currently being developed.

Configuration

In Arti, the memory quota tracker is controlled by the [system.memory] configuration subsection in arti.toml. You can enable it by writing something like this:

[system]
memory.max = "1 GiB"

The feature is compiled in by default. Setting the limit for the first time requires an Arti restart. After that, adjusting (or removing) the limit can be done at runtime.

There is also a memory.low_water setting: When Arti needs to free memory because max is exceeded, it keeps tearing down connections until the usage is below low_water. This hysteresis helps stop the system oscillating. The defaualt value of low_water is 75% of max.

(Note that unlike C Tor's MaxMemInQueues setting, the current default in Arti is not to enable a memory limit. In Arti you must turn on the feature explicitly, by setting max. We hope to get more experience of how it works for users in practice, before we consider whether to enable a limit by default.)

Logging

After you've enabled memory quota tracking, you should see Arti print a log message like this:

2024-10-31T16:55:55Z  INFO tor_memquota::mtracker: memory quota tracking initialised max=1.00 GiB low_water=768 MiB

You can tell if memory reclaim has been triggered:

2024-10-31T17:22:19Z  INFO tor_memquota::mtracker::reclaim: memory tracking: 1.86 GiB > 1.00 GiB, reclamation started (target 768 MiB)
...
2024-10-31T17:22:20Z  INFO tor_memquota::mtracker::reclaim: memory tracking reclamation reached: 44.3 KiB (target 768 MiB): complete

Caution: very new code!

This is a very new feature. There is a lot of complexity behind the scenes, and by its nature it is difficult to do a full-scale integration test. It is quite possible that there are bugs! We'd like to hear your feedback, when you enable this feature.

You can report issues you discover in our gitlab (also available via an anonymous ticket reporting system). You can also contact us informally by email, or on irc: we're in #tor-dev on OFTC.

Thanks to our sponsors

Thanks to Zcash Community Grants for their funding, which enabled the development of this feature, and of course to our other sponsors for funding the development of Arti.

Comments

We encourage respectful, on-topic comments. Comments that violate our Code of Conduct will be deleted. Off-topic comments may be deleted at the discretion of the moderators. Please do not comment as a way to receive support or to report bugs on a post unrelated to a release. If you are looking for support, please see our FAQ, user support forum or ways to get in touch with us.