diff --git a/DockerfileLeanFoundation b/DockerfileLeanFoundation index 64c6209db3cf..620e8cb5edd2 100644 --- a/DockerfileLeanFoundation +++ b/DockerfileLeanFoundation @@ -334,7 +334,10 @@ RUN conda install -c nvidia -y cuda-compiler=12.8.1 && conda clean -y --all ENV MKL_THREADING_LAYER=GNU ENV CUDA_MODULE_LOADING=LAZY ENV XLA_FLAGS=--xla_gpu_cuda_data_dir=/opt/miniconda3/ -ENV LD_LIBRARY_PATH=/opt/miniconda3/lib/python3.11/site-packages/nvidia/nvjitlink/lib/:/opt/miniconda3/lib/python3.11/site-packages/nvidia/cuda_nvrtc/lib/:$LD_LIBRARY_PATH +# conda's OpenSSL first: .NET loads libssl/libcrypto by soname before python, and conda extensions then bind to that same copy, +# which must be conda's. Only these two are linked so every other library keeps resolving as before +RUN mkdir -p /opt/conda-openssl/lib && ln -s /opt/miniconda3/lib/libssl.so.3 /opt/miniconda3/lib/libcrypto.so.3 /opt/conda-openssl/lib/ +ENV LD_LIBRARY_PATH=/opt/conda-openssl/lib/:/opt/miniconda3/lib/python3.11/site-packages/nvidia/nvjitlink/lib/:/opt/miniconda3/lib/python3.11/site-packages/nvidia/cuda_nvrtc/lib/:$LD_LIBRARY_PATH # reduces GPU memory usage ENV PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True diff --git a/Tests/Python/PythonPackagesTests.cs b/Tests/Python/PythonPackagesTests.cs index 10360f498a97..e745078601e6 100644 --- a/Tests/Python/PythonPackagesTests.cs +++ b/Tests/Python/PythonPackagesTests.cs @@ -15,6 +15,9 @@ */ using System; +using System.IO; +using System.Linq; +using System.Security.Cryptography; using Python.Runtime; using NUnit.Framework; @@ -2945,6 +2948,49 @@ import pandas as pd report = sv.analyze(df, target_feat='target')"); } + [Test] + public void Cryptography() + { + AssertCode(@" +from cryptography.fernet import Fernet + +def RunTest(): + cipher = Fernet(Fernet.generate_key()) + message = b'LEAN' + assert cipher.decrypt(cipher.encrypt(message)) == message"); + } + + // Runs first: a python extension loading the environment's libssl earlier would hide a wrong library search path + [Test, Order(1)] + public void DotNetLoadsPythonEnvironmentOpenSsl() + { + if (!OS.IsLinux) + { + Assert.Ignore("Only Linux resolves OpenSSL by soname"); + } + + // .NET loads libssl and libcrypto by soname, and python extensions loaded later bind to those same copies, + // so they need to be the python environment's, which its extensions were built against + SHA256.HashData(new byte[1]); + + string pythonPrefix; + using (Py.GIL()) + { + using var sys = Py.Import("sys"); + // a virtual environment keeps using its base installation's native libraries + pythonPrefix = sys.GetAttr("base_prefix").As(); + } + var openSslLibraries = File.ReadLines("/proc/self/maps") + .Select(line => line.Split(' ', StringSplitOptions.RemoveEmptyEntries).Last()) + .Where(path => path.Contains("/libssl.so") || path.Contains("/libcrypto.so")) + .Distinct() + .ToList(); + + Assert.IsNotEmpty(openSslLibraries); + Assert.IsTrue(openSslLibraries.All(path => path.StartsWith(pythonPrefix, StringComparison.InvariantCulture)), + $"OpenSSL loaded outside of {pythonPrefix}: {string.Join(", ", openSslLibraries)}"); + } + [TestCase("tf2onnx", "1.16.1", "__version__"), Explicit("These need to be run by themselves")] [TestCase("skl2onnx", "1.19.1", "__version__")] [TestCase("onnxmltools", "1.14.0", "__version__")]