[tor-bugs] #25821 [Core Tor/Stem]: Stem getconf cache doesn't clear for CONF_CHANGED events; probably should set value

Tor Bug Tracker & Wiki blackhole at torproject.org
Mon Apr 16 20:41:32 UTC 2018


#25821: Stem getconf cache doesn't clear for CONF_CHANGED events; probably should
set value
---------------------------+------------------------
 Reporter:  dmr            |          Owner:  atagar
     Type:  defect         |         Status:  new
 Priority:  Medium         |      Milestone:
Component:  Core Tor/Stem  |        Version:
 Severity:  Normal         |     Resolution:
 Keywords:                 |  Actual Points:
Parent ID:                 |         Points:
 Reviewer:                 |        Sponsor:
---------------------------+------------------------

Comment (by dmr):

 Here's the code as of
 [[https://gitweb.torproject.org/stem.git/tree/stem/control.py?id=82b22046f40f49579ff37c4247db50050334998a#n1071|`82b22046f40f49579ff37c4247db50050334998a`]]:
 {{{
 #!python
     def _confchanged_listener(event):
       if self.is_caching_enabled():
         self._set_cache(dict((k, None) for k in event.config), 'getconf')

         # ...

     self.add_event_listener(_confchanged_listener, EventType.CONF_CHANGED)
 }}}

 This code actually usually won't clear the cache when it intends to. The
 cache key is `lower()`ed when set in `set_options()`
 ([[https://gitweb.torproject.org/stem.git/tree/stem/control.py?id=82b22046f40f49579ff37c4247db50050334998a#n2402|code
 link]]):
 {{{
 #!python
       if self.is_caching_enabled():
         to_cache = {}

         for param, value in params:
           param = param.lower()

           # ...

           to_cache[param] = value

           # ...

         # reset any getinfo parameters that can be changed by a SETCONF

         self._set_cache(dict([(k.lower(), None) for k in
 CACHEABLE_GETINFO_PARAMS_UNTIL_SETCONF]), 'getinfo')
         self._set_cache(None, 'listeners')

         self._set_cache(to_cache, 'getconf')
         self._set_cache({'get_custom_options': None})
 }}}
 ... but we can't guarantee that the event we receive will have a given
 case. In my tests, it was always the canonical torrc case, but that's not
 concretely specified in the control spec
 ([[https://gitweb.torproject.org/torspec.git/tree/control-
 spec.txt?id=d4a64fbf5aaba383638d9f3c70bd2951f8c5ad89#n259|"most keywords
 are case-sensitive")]].

 At first, this looks like a simple change to fix (use `k.lower()`), but
 when we consider what happens when a config options is set, we see it's
 not so straightforward - the cache gets cleared.

 Here's code for an adhoc test:
 {{{
 #!python
 # run this example interactively (don't copy/paste everything) in tor-
 prompt
 def print_getconf_cache_contents():
   global controller

   with controller._cache_lock:
     cache_keys = [k for k in controller._request_cache.keys() if
 k.startswith('getconf.')]
     if cache_keys:
       print("printing cache")
       for k in cache_keys:
         value = controller._request_cache[k]
         print("config cache %s has value: %s" % (k, value))
     else:
       print("cache is empty")

 def print_conf_event_and_cache(event):
   # blank line to make prettier at a prompt
   print('')

   for k,v in event.config.items():
     print("Config %s set to: %s" % (k, v))

   print_getconf_cache_contents()

 # in tor-prompt, controller is simply available for us to use
 controller.add_event_listener(print_conf_event_and_cache,
 stem.control.EventType.CONF_CHANGED)

 # for example simplicity WAIT for the output between each set_conf() call
 controller.set_conf('ContactInfo', 'hi there')
 # ^ generate output via our listener
 # with k.lower() implementation, note that the cache doesn't have
 'getconf.contactinfo'

 # simulate a SETCONF from another controller
 SETCONF ContactInfo="hello back"
 # ^ generate output via our listener
 # with unchanged implementation, note that the cache is still set to "hi
 there"
 # with k.lower() implementation, note that the cache doesn't have
 'getconf.contactinfo'

 # simulate a SETCONF from another controller
 SETCONF ContactInfo=
 # ^ generate output via our listener
 # with unchanged implementation, note that the cache is still set to "hi
 there"
 # with k.lower() implementation, note that the cache doesn't have
 'getconf.contactinfo'

 # clean up
 controller.remove_event_listener(print_conf_event_and_cache)
 }}}

 To do a simple change to the implementation, change this line in
 `_confchanged_listener()` from:
 {{{
 #!python
         self._set_cache(dict((k, None) for k in event.config), 'getconf')
 }}}
 to:
 {{{
 #!python
         self._set_cache(dict((k.lower(), None) for k in event.config),
 'getconf')
 }}}
 (adding `.lower()` after `k`)

 You can see that the cache doesn't clear, but when it does with a simple
 `.lower()` change... it doesn't really retain data for long (because the
 `set_conf()` causes a `CONF_CHANGED`, which clears the cache).

--
Ticket URL: <https://trac.torproject.org/projects/tor/ticket/25821#comment:1>
Tor Bug Tracker & Wiki <https://trac.torproject.org/>
The Tor Project: anonymity online


More information about the tor-bugs mailing list