Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lru.LRU is not thread-safe; session cache maintenance (web3._utils.request) can crash the interpreter #1847

Closed
jamadden opened this issue Jan 22, 2021 · 9 comments

Comments

@jamadden
Copy link

jamadden commented Jan 22, 2021

  • Version: 5.15.0, but current master too.
  • Python: Any
  • OS: Any

What was wrong?

In #876, code was added to close sessions as they are evicted from the session cache. But in the presence of multiple threads this can lead to interpreter crashes, because lru.LRU is not thread safe. If, while running the callback function a thread switch happens, and that other thread also attempts to add a new session or otherwise manipulate the session cache, the interpreter can crash. (When we re-enter the lru.LRU object, the internal state of the object is inconsistent while the callback is running. Because the callback is arbitrary Python code, it can easily drop the GIL.)

This was originally reported in rotki/rotki#2132. There, they're using greenlets, but its easy to demonstrate the crash also using threads (a sample script is in that issue).

How can it be fixed?

Adding a lock around uses of _session_cache ought to fix the problem.

LefterisJP added a commit to LefterisJP/rotkehlchen that referenced this issue Jan 22, 2021
Should fix the segfaults we saw in gevent due to the non-thread safe
use of LRU cache in web3.py request's session cache.

As very well explained here by @jamadden:
rotki#2132 (comment)

This can all be reverted once
ethereum/web3.py#1847 is fixed in web3.py side.
@kclowes
Copy link
Collaborator

kclowes commented Jan 22, 2021

Wow, thanks for the report @jamadden! Will put this near the top of our todo list.

@Eenae
Copy link

Eenae commented Apr 1, 2022

The issue occurred in our setup (> 8 threads, various endpoints) quite stably. Synchronizing the cache access indeed solved the problem.

@kclowes
Copy link
Collaborator

kclowes commented Apr 1, 2022

@Eenae when you say:

Synchronizing the cache access indeed solved the problem.

Do you mean PR #2407 fixed your issue?

@Eenae
Copy link

Eenae commented Apr 1, 2022

@kclowes no, it seems #2407 is not a PR.
Did it via a monkey-patch:

from typing import Callable
import threading
import web3._utils.request

lock = threading.Lock()

def locked(fn: Callable) -> Callable:
    def inner(*args, **kwargs):
        with lock:
            return fn(*args, **kwargs)
    return inner

web3._utils.request.cache_session = locked(web3._utils.request.cache_session)
web3._utils.request._get_session = locked(web3._utils.request._get_session)

@kclowes
Copy link
Collaborator

kclowes commented Apr 1, 2022

Oops, I meant #2409. I think that's about equivalent to what you have. Thanks for the example!

@fselmo
Copy link
Collaborator

fselmo commented Aug 16, 2022

Resolved in #2409 for web3.py v6. Needs a back-port to v5 PR.

@fselmo fselmo closed this as completed Aug 16, 2022
@LefterisJP
Copy link

Hey @fselmo sorry for the ping but has this been backported to v5? And if yet in which release?

@fselmo
Copy link
Collaborator

fselmo commented Dec 1, 2022

Hey @LefterisJP, this was added to v5 in #2691 but there is not yet a v5 version out with these changes in, it only exists on the master branch as of now. I'll see if we can change that asap. Thanks for the ping!

edit: Just released in web3.py v5.31.2

@LefterisJP
Copy link

Thank you so much @fselmo. That was a really fast response and action. Appreciate it!

LefterisJP added a commit to LefterisJP/rotkehlchen that referenced this issue Dec 4, 2022
The awesome guys at web3.py included the fix for
ethereum/web3.py#1847 in that release
LefterisJP added a commit to rotki/rotki that referenced this issue Dec 4, 2022
The awesome guys at web3.py included the fix for
ethereum/web3.py#1847 in that release
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants