X Tutup
The Wayback Machine - https://web.archive.org/web/20230327095248/https://github.com/python/cpython/issues/94637
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

SSLContext.set_default_verify_paths holds GIL for > 1s #94637

Closed
efroemling opened this issue Jul 6, 2022 · 4 comments
Closed

SSLContext.set_default_verify_paths holds GIL for > 1s #94637

efroemling opened this issue Jul 6, 2022 · 4 comments
Labels
3.10 bug and security fixes 3.11 bug and security fixes 3.12 new features, bug and security fixes expert-SSL performance Performance or resource usage

Comments

@efroemling
Copy link

Hi; I'm the author of a game engine which makes heavy use of Python. The engine sends various network requests in background threads for various purposes. Recently I switched these to use https instead of http, and soon after noticed my Android builds in particular started hitching badly.

I tracked this down to my main logic thread occasionally spending upwards of 1 second waiting for a GIL lock, and further tracked that down to SSLContext.set_default_verify_paths(). That gets called by the urllib requests I'm firing off in my bg thread and winds up starving all other threads as it holds on to the GIL for the entirety of the SSL_CTX_set_default_verify_paths() call, which for some reason is taking quite a while on some Android devices.

I can separately look into why the underlying call is taking so long in the Android case, but regardless I'm able to completely eliminate the hitches by releasing the GIL for that call (enclosing the SSL_CTX_set_default_verify_paths() call with PySSL_BEGIN_ALLOW_THREADS/PySSL_END_ALLOW_THREADS).

Is that safe and reasonable to do for that call? I'd be happy to make a PR if so.

This was all tested on Python 3.10.5.

Thanks
-Eric

cpython/Modules/_ssl.c

Lines 4301 to 4310 in 760b8cf

static PyObject *
_ssl__SSLContext_set_default_verify_paths_impl(PySSLContext *self)
/*[clinic end generated code: output=0bee74e6e09deaaa input=35f3408021463d74]*/
{
if (!SSL_CTX_set_default_verify_paths(self->ctx)) {
_setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__);
return NULL;
}
Py_RETURN_NONE;
}

@tiran
Copy link
Member

tiran commented Jul 7, 2022

Yes, it is safe. I wonder, why SSL_CTX_set_default_verify_paths does not release the GIL in the first place. Probably an oversight. It is a very costly call that performs I/O and expensive computational tasks. It loads all root CA certificates from disk, parses + validates them, and puts them into a lookup data structure.

You can also optimize the code and use a single SSLContext object for all network code. It is safe to share a single instance across threads if you don't modify the object (load more certs, change verification parameters, options, etc.).

@efroemling
Copy link
Author

You can also optimize the code and use a single SSLContext object for all network code. It is safe to share a single instance across threads if you don't modify the object (load more certs, change verification parameters, options, etc.).

Good to know; thanks! Even with the GIL-related hitches fixed, that would probably be worth it for me to do to shave that time off of individual network round trips. I'm assuming this would be done by explicitly passing in the SSLContext for each particular use? Or is there any higher level way to get things to share a single context by default?

And on a related note, is there any clean way to point all contexts at a root cert file by default? Since Android doesn't seem to have certs in a consistent place I'm bundling ones from the Python certifi package and using the SSL_CERT_FILE env var to point SSL at them, which gets them picked up by all contexts, but I didn't know if there's a more 'correct' way to do that sort of thing.

@tiran
Copy link
Member

tiran commented Jul 7, 2022

All stdlib network modules accept a context or ssl_context argument, e.g. urllib.request.urlopen(context=...). You have to pass a context explicitly.

I recommend that users always create a context with ssl.create_default_context(). It will either load certificates from default location (and use SSL_CERT_FILE) or you can pass arguments to load a cafile or capath instead. You can load additional trust anchors with ctx.load_verify_locations().

@efroemling
Copy link
Author

efroemling commented Jul 7, 2022

Ok; sounds good. I'll just pass in an explicit shared context created from ssl.create_default_context() in places I want to speed up then. And it sounds like SSL_CERT_FILE is the right way to go to get said default contexts doing the right thing. 👍

One obscure note in case its helpful: on my Android OpenSSL build, SSL_CERT_FILE wasn't actually getting read by default. OpenSSL does a ossl_safe_getenv() call which was disallowing its use for some reason. But ssl.get_default_verify_paths() in Python incorrectly listed it as a verify path. I don't know if it would be possible or worth the energy to have ssl.get_default_verify_paths() tap into that same logic to keep things consistent, but I just thought I'd mention it. (And I wound up just forcing my Android SSL build to always accept env vars to get it working in my case).

miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jul 9, 2022
…ythonGH-94658)

(cherry picked from commit 78307c7)

Co-authored-by: Christian Heimes <christian@python.org>
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jul 9, 2022
…ythonGH-94658)

(cherry picked from commit 78307c7)

Co-authored-by: Christian Heimes <christian@python.org>
miss-islington added a commit that referenced this issue Jul 9, 2022
(cherry picked from commit 78307c7)

Co-authored-by: Christian Heimes <christian@python.org>
@tiran tiran added performance Performance or resource usage 3.11 bug and security fixes 3.10 bug and security fixes 3.12 new features, bug and security fixes labels Jul 9, 2022
@tiran tiran closed this as completed Jul 9, 2022
miss-islington added a commit that referenced this issue Jul 9, 2022
(cherry picked from commit 78307c7)

Co-authored-by: Christian Heimes <christian@python.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.10 bug and security fixes 3.11 bug and security fixes 3.12 new features, bug and security fixes expert-SSL performance Performance or resource usage
Projects
None yet
Development

No branches or pull requests

3 participants
X Tutup