commit 87cea952a27b024981e70efacc09253e22a7658f Author: Damian Johnson atagar@torproject.org Date: Wed May 8 11:05:15 2019 -0700
Fix ExitPolicy thread safety bug
Interesting catch from juga! I don't have a repro for the stacktrace they cite, but the only way our input_rules could potentially be None is if two threads attempt to parse the input_rules at the same time.
Creating a local reference for the input_rules to avoid this while remaining lock-free...
https://trac.torproject.org/projects/tor/ticket/29899 --- docs/change_log.rst | 1 + stem/exit_policy.py | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/docs/change_log.rst b/docs/change_log.rst index 44153d55..cdb9e450 100644 --- a/docs/change_log.rst +++ b/docs/change_log.rst @@ -51,6 +51,7 @@ The following are only available within Stem's `git repository * Added :func:`~stem.control.Controller.get_uptime` method to the :class:`~stem.control.Controller` * Controller events could fail to be delivered in a timely fashion (:trac:`27173`) * Adjusted :func:`~stem.control.Controller.get_microdescriptors` fallback to also use '.new' cache files (:trac:`28508`) + * ExitPolicies could raise TypeError when read concurrently (:trac:`29899`) * **DORMANT** and **ACTIVE** :data:`~stem.Signal` (:spec:`4421149`)
* **Descriptors** diff --git a/stem/exit_policy.py b/stem/exit_policy.py index f02ec146..5bdadf9b 100644 --- a/stem/exit_policy.py +++ b/stem/exit_policy.py @@ -452,14 +452,19 @@ class ExitPolicy(object): return ExitPolicy(*[rule for rule in self._get_rules() if not rule.is_default()])
def _get_rules(self): - if self._rules is None: + # Local reference to our input_rules so this can be lock free. Otherwise + # another thread might unset our input_rules while processing them. + + input_rules = self._input_rules + + if self._rules is None and input_rules is not None: rules = [] is_all_accept, is_all_reject = True, True
- if isinstance(self._input_rules, bytes): - decompressed_rules = zlib.decompress(self._input_rules).split(b',') + if isinstance(input_rules, bytes): + decompressed_rules = zlib.decompress(input_rules).split(b',') else: - decompressed_rules = self._input_rules + decompressed_rules = input_rules
for rule in decompressed_rules: if isinstance(rule, bytes):
tor-commits@lists.torproject.org