diff --git a/.github/workflows/cache_libs.yml b/.github/workflows/cache_libs.yml index e962af32..10dc803b 100644 --- a/.github/workflows/cache_libs.yml +++ b/.github/workflows/cache_libs.yml @@ -20,7 +20,7 @@ on: required: false type: string XMLSEC1_VERSION: - default: "1.3.10" + default: "1.3.11" required: false type: string ZLIB_VERSION: diff --git a/build_support/lib_xmlsec_dependency_builder.py b/build_support/lib_xmlsec_dependency_builder.py index 5e3911c5..eca8787e 100644 --- a/build_support/lib_xmlsec_dependency_builder.py +++ b/build_support/lib_xmlsec_dependency_builder.py @@ -50,7 +50,7 @@ class LibXmlsecDependencyBuilder: 'libxml2_version': '2.14.6', # Make sure it matches with lxml 'libxslt_version': '1.1.43', 'openssl_version': '3.6.0', - 'xmlsec1_version': '1.3.10', + 'xmlsec1_version': '1.3.11', 'zlib_version': '1.3.1', } WINDOWS_DEFAULT_LIB_VERSIONS: ClassVar[dict[str, str]] = { diff --git a/doc/source/modules/xmlsec.rst b/doc/source/modules/xmlsec.rst index 426bbaa4..4d39dc67 100644 --- a/doc/source/modules/xmlsec.rst +++ b/doc/source/modules/xmlsec.rst @@ -1,6 +1,17 @@ ``xmlsec`` ---------- +Lifecycle +~~~~~~~~~ + +The module initializes the underlying xmlsec library on import. Applications +that call :func:`xmlsec.shutdown` should treat it as process-final and should +not call :func:`xmlsec.init` afterwards. + +This is required because upstream xmlsec1 versions starting with 1.3.11 may +call ``OPENSSL_cleanup()`` during shutdown when using the OpenSSL backend. +OpenSSL cannot be reinitialized in the same process after that cleanup has run. + .. automodule:: xmlsec :members: :undoc-members: diff --git a/src/main.c b/src/main.c index 61eac139..c7dac2b5 100644 --- a/src/main.c +++ b/src/main.c @@ -101,8 +101,11 @@ static int PyXmlSec_Init(void) { static char PyXmlSec_PyInit__doc__[] = \ "init() -> None\n" "Initializes the library for general operation.\n\n" - "This is called upon library import and does not need to be called\n" - "again :func:`~.shutdown` is called explicitly).\n"; + "This is called upon library import and normally does not need to be\n" + "called explicitly. It is only valid before shutdown() has been called.\n\n" + "Calling init() after shutdown() is unsupported because upstream\n" + "xmlsec1 1.3.11+ may call OPENSSL_cleanup() during shutdown, and OpenSSL\n" + "cannot be reinitialized in the same process after that cleanup.\n"; static PyObject* PyXmlSec_PyInit(PyObject *self) { if (PyXmlSec_Init() < 0) { return NULL; @@ -114,7 +117,11 @@ static char PyXmlSec_PyShutdown__doc__[] = \ "shutdown() -> None\n" "Shutdowns the library and cleanup any leftover resources.\n\n" "This is called automatically upon interpreter termination and\n" - "should not need to be called explicitly."; + "should not need to be called explicitly.\n\n" + "Shutdown is process-final. Do not call init() after shutdown(),\n" + "because upstream xmlsec1 1.3.11+ may call OPENSSL_cleanup() during shutdown,\n" + "and OpenSSL cannot be reinitialized in the same process after that\n" + "cleanup."; static PyObject* PyXmlSec_PyShutdown(PyObject* self) { PyXmlSec_Free(free_mode); Py_RETURN_NONE; diff --git a/tests/conftest.py b/tests/conftest.py index a65235d5..4d57ef10 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,10 +1,11 @@ def pytest_collection_modifyitems(items): - """Put the module init test first. + """Put the module shutdown test last. - This way, we implicitly check whether any subsequent test fails because of module reinitialization. + xmlsec shutdown is process-final with OpenSSL cleanup introduced in + xmlsec1 1.3.11, so no tests should use xmlsec after it runs. """ - def module_init_tests_first(item): - return int('test_xmlsec.py::TestModule::test_reinitialize_module' not in item.nodeid) + def module_init_shutdown_tests_last(item): + return int('test_xmlsec.py::TestModule::test_init_shutdown_module' in item.nodeid) - items.sort(key=module_init_tests_first) + items.sort(key=module_init_shutdown_tests_last) diff --git a/tests/test_xmlsec.py b/tests/test_xmlsec.py index 52dce2b3..4267ac2a 100644 --- a/tests/test_xmlsec.py +++ b/tests/test_xmlsec.py @@ -3,11 +3,14 @@ class TestModule(base.TestMemoryLeaks): - def test_reinitialize_module(self): - """This test doesn't explicitly verify anything, but will be invoked first in the suite. + iterations = 0 - So if the subsequent tests don't fail, we know that the ``init()``/``shutdown()`` - function pair doesn't break anything. + def test_init_shutdown_module(self): + """Check explicit initialization before final module shutdown. + + This test is invoked last because shutdown is process-final: since + xmlsec1 1.3.11, its OpenSSL backend may call OPENSSL_cleanup(), after + which OpenSSL cannot be reinitialized in the same process. """ - xmlsec.shutdown() xmlsec.init() + xmlsec.shutdown()