Skip to content

Segfault when trying to call ExDataFreeOcspResponse after .NET module has been unloaded #123140

Closed

Description

@ulrichb

Description

TL;DR: In EnsureOpenSsl11Initialized(), free_func pointers are registered which get dangling after the .NET module has been unloaded. When OpenSSL later tries to call this free_func, we get a segfault. It seems that CRYPTO_free_ex_index() calls are missing.

We have the following process/module scenario:

  • OpenSSL is the entrypoint (openssl cms ... command with -engine pkcs11).
  • => The libp11 OpenSSL engine (pkcs11.so) gets dynamically loaded by OpenSSL.
  • By configuration, libp11 dynamically loads our PKCS#11 module (libSignPath.Cryptoki.so)
  • (Note that the following segmentation fault can only reproduced with recent OpenSSL and libp11 versions, e.g. on Debian 13 with OpenSSL 3.5.4 and libp11 0.4.13.)

The segmentation fault happens during unloading (when OpenSSL's cms_main calls CMS_ContentInfo_free()).

Full stack trace (linked OpenSSL is 3.5.4)
func                        file                            addr
??                                                          0x7acfe4f0dc70   
CRYPTO_free_ex_data         crypto/ex_data.c:406            0x7acfe6695813  
x509_cb                     crypto/x509/x_x509.c:85         0x7acfe6858665  
ossl_asn1_item_embed_free   crypto/asn1/tasn_fre.c:117      0x7acfe6520842  
ossl_asn1_template_free     crypto/asn1/tasn_fre.c:146      0x7acfe6520980  
ossl_asn1_item_embed_free   crypto/asn1/tasn_fre.c:70       0x7acfe652064f  
ossl_asn1_template_free     crypto/asn1/tasn_fre.c:141      0x7acfe6520923  
ossl_asn1_item_embed_free   crypto/asn1/tasn_fre.c:114      0x7acfe6520805  
ossl_asn1_template_free     crypto/asn1/tasn_fre.c:146      0x7acfe6520980  
ossl_asn1_item_embed_free   crypto/asn1/tasn_fre.c:114      0x7acfe6520805  
ASN1_item_free              crypto/asn1/tasn_fre.c:20       0x7acfe652046a  
CMS_ContentInfo_free        crypto/cms/cms_lib.c:27         0x7acfe659d29b  
cms_main                    apps/cms.c:1320                 0x5fb2c34d66ea  
do_cmd                      apps/openssl.c:428              0x5fb2c34f0c50  
main                        apps/openssl.c:309              0x5fb2c34f0812  

When the segfault happens, the mentioned .NET module libSignPath.Cryptoki.so already got unloaded (confirmed via LD_DEBUG=libs tracing).

On the stack trace, OpenSSL is in the process of freeing X.509 objects, which it used in the openssl cms command. On these X.509 objects, .NET attached OpenSSL "exdata" (registered at EnsureOpenSsl11Initialized() (using class index CRYPTO_EX_INDEX_X509). .NET registers ExDataFreeOcspResponse() as free_func.

Now OpenSSL (in the process of freeing X.509 objects) tries to call the registered free_func, which got unloaded. => Segfault.

We confirmed this by remembering the free_func pointer in ossl_crypto_get_ex_new_index_ex() for ExDataFreeOcspResponse() and seeing that the segfault happens due to accessing exactly this address.

So the problem is that .NET does not unregister the registered free_funcs. That is possible via CRYPTO_free_ex_index(), which is also recommended in the OpenSSL docs "if a dynamic library can be unloaded":

If a dynamic library can be unloaded, it should call CRYPTO_free_ex_index() when this is done. This will replace the callbacks with no-ops so that applications don't crash. Any existing exdata will be leaked.

Reproduction Steps

See above for a description of the necessary steps. Unfortunately we don't have a minimal reproduction sample.

Expected behavior

No segfault 😆

Actual behavior

See above.

Regression?

No response

Known Workarounds

A (very) hacky workaround is to run CRYPTO_free_ex_index(3, 1 /* index may vary! */); CRYPTO_free_ex_index(2, 1 /* index may vary! */); from the .NET process before module unloading.

Configuration

.NET 9 (AOT) in Debian 13 Linux container, AMD64.

Other information

No response

Activity

  1. added
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Jan 13, 2026
  2. jkotas commented on Jan 13, 2026

    @jkotas
    Member

    So the problem is that .NET does not unregister the registered free_funcs. That is possible via CRYPTO_free_ex_index(), which is also recommended in the OpenSSL docs "if a dynamic library can be unloaded":

    .NET runtime does not support OpenSSL being unloaded.

  3. added and removed
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Jan 13, 2026
  4. dotnet-policy-service commented on Jan 13, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones
    See info in area-owners.md if you want to be subscribed.

  5. ulrichb commented on Jan 13, 2026

    @ulrichb
    Author

    .NET runtime does not support OpenSSL being unloaded.

    That's totally understandable. Here it's the other way round: the (AOT compiled) .NET module gets unloaded (OpenSSL still loaded and later running the dangling free_func).

  6. jkotas commented on Jan 13, 2026

    @jkotas
    Member

    .NET runtime (including binaries published with NativeAOT) does not support unloading. #64629 and linked issues have discussion about it.

  7. ulrichb commented on Feb 3, 2026

    @ulrichb
    Author

    Okay, I understand that CoreCLR doesn't fully/officially support (shared library) unloading (i.e. dlclose()).

    In our case -- where a .NET AOT library is unloaded indirectly -- we unfortunately have no control about the unloading behavior.

    Unloading works in all our integration test cases, except the openssl cms scenario described above b/c of order of unloading, producing this segfault.

    So I'd ask whether we could add "partial unloading support" by addressing this very specific issue by calling CRYPTO_free_ex_index() before unloading as suggested in the OpenSSL docs.

    The reason I'm asking is that it's very difficult to fix this issue "from the outside" b/c it's hard to get the g_x509_ocsp_index value.

  8. jkotas commented on Feb 3, 2026

    @jkotas
    Member

    Unloading works in all our integration test cases, except the openssl cms scenario described above b/c of order of unloading,

    I expect that you are going to see other types of crashes once you get this one out of the way. Fixing them all is very difficult.

  9. ulrichb commented on Feb 4, 2026

    @ulrichb
    Author

    Indeed this is the only issue (in our scenario). We proofed this by applying the CRYPTO_free_ex_index()-workaround mentioned above. (When applying the workaround shutdown runs w/o any failures).

  10. jeffhandley commented on Aug 16, 2026

    @jeffhandley
    Member

    Verified this is fixed by #131960 (merged for 11.0-rc1), which links NativeAOT shared libraries with -Wl,-z,nodelete. With DF_1_NODELETE set, dlclose() on the .NET .so becomes a no-op, so ExDataFreeOcspResponse can never dangle.

    Confirmed with a minimal repro on Debian 13 / OpenSSL 3.5.6 — a NativeAOT .so loaded by a C host, dlclosed, then X509_free:

    SDK Result
    9.0.317 SIGSEGV in CRYPTO_free_ex_data; .so unmapped after dlclose
    10.0.400 identical SIGSEGV
    11.0.100-rc.1.26413.103 no crash; .so still mapped

    Nightly verified: 11.0.100-rc.1.26413.103 (commit b8c18a5c2f, ILCompiler 11.0.0-rc.1.26413.103) — dated 2026-08-13, after the 2026-08-08 merge, and the -Wl,-z,nodelete line is physically present in that build's Microsoft.NETCore.Native.Unix.targets.

    .NET still never calls CRYPTO_free_ex_index, and .NET 11 still registers the X509 ex_index. #131960 doesn't unregister the callback — it makes the library un-unloadable so the pointer stays valid.

  11. removed
    untriagedNew issue has not been triaged by the area owner
    on Aug 16, 2026
  12. added this to the 11.0.0 milestone on Aug 16, 2026
  13. ulrichb commented on Aug 16, 2026

    @ulrichb
    Author

    @jeffhandley That's great news! Many thanks for reproducing and testing against .NET 11 RC1 ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions