[tor-commits] [stem/master] Fix ExitPolicy thread safety bug

atagar at torproject.org atagar at torproject.org
Wed May 8 18:11:25 UTC 2019


commit 87cea952a27b024981e70efacc09253e22a7658f
Author: Damian Johnson <atagar at 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):



More information about the tor-commits mailing list