Fix session logger use-after-free during EP teardown under VERBOSE logging
Description
Move owned_session_logger_ declaration before execution_providers_ in InferenceSession so the logger outlives EPs during member destruction.
C++ destroys members in reverse declaration order. Previously:
~owned_session_logger_ (L905) // logger freed
↓
~execution_providers_ (L745) // EP teardown logs via dangling pointer → crash
After this change:
~execution_providers_ (L750) // EP teardown logs safely ✅
↓
~owned_session_logger_ (L744) // logger freed, no remaining users
Motivation and Context
Plugin EPs receive an OrtLogger* via OrtEpFactory::CreateEp(). During session destruction, EP teardown callbacks (e.g., ReleaseNodeComputeInfos) may log through this pointer. Because owned_session_logger_ was declared after execution_providers_, the logger was already freed when EPs destructed — a use-after-free that crashes deterministically under VERBOSE logging.
Affects all Plugin EPs that log in any teardown path. Reproduced with OpenVINO Plugin EP via webnn_graph_impl_fuzzer at VERBOSE level.
Fixes https://github.com/microsoft/onnxruntime/issues/28234
Tests Added
Added regression tests in onnxruntime/test/framework/inference_session_test.cc:
LoggingOnDestroyExecutionProvider— A mock EP that logs via its stored logger pointer in its destructor. If the logger has been freed, this triggers a use-after-free (detected by ASan or as a segfault).SessionLoggerOutlivesEPsOnDestruction— Creates a session with VERBOSE logging and the mock EP, then destroys the session. Verifies that the logger was valid during EP teardown and that the teardown log message was captured.SessionLoggerOutlivesEPsWithMultipleEPs— Same scenario with two mock EPs (distinct type names) to confirm all registered EPs can safely log during teardown.
Verification
Confirmed the tests are effective regression tests:
| Scenario | Result |
|---|---|
| With fix (logger declared before EPs) | Both tests pass ✅ |
| Without fix (logger declared after EPs, original bug) | SessionLoggerOutlivesEPsOnDestruction crashes with Segmentation fault (exit code 139) — use-after-free ❌ |
This proves the member declaration order is the critical factor, and the tests will catch any future regression that reorders these members.