[BUG]: Session context handles can collide after vector erase and slot reuse
@garydan42 is already working on this.
Since Sep 1, 2026.
- Dominant language
- C++
- Stars
- 680
- Forks
- 73
- Avg merge
- 4h 51m
- Merged PRs (30d)
- 59
Description
Summary
Following Pete's request to recheck current source and track separately, this report focuses specifically on context-handle identity in CMidiSessionTracker, not on establishing the cause of #564 or my application's MIDI failure.
AI Generated Content: The technical analysis and standalone model below were prepared with OpenAI Codex. The model was compiled and executed locally with MSVC and rerun for this report. The personal observations below are verbatim excerpts of my own messages from the original incident. No real-service reproduction or installed-binary/source equivalence is claimed.
The new src/in-box/Service/Exe path was inspected at current main commit 4c8444661b1cc6ee7c88f2a083459baf6bf8508d. In that revision, session context handles still derive from addresses of entries in a std::vector. Erasing an earlier entry and then adding another session can give two live sessions the same saved context token. Lookup during context rundown can consequently select the wrong session.
Pete mentioned forthcoming changes that remove pointer-based identity and address locking. If a different commit or branch already replaces this particular session-context mechanism, please point us to it; this report is scoped to the public revision above.
Your personal observation that led to the bug report
Verbatim excerpts from my original incident notes/messages, not AI-written observations:
My current running app stopped responding to midi inputs. The app shows as ready, the vst looks loaded, I can open the vst editor, but no indication it is responding to the midi inputs. I have not tried changing patches or anything to restore it. I have left it in this state to try and troubleshoot
yes, fully closing and relaunching has cleared the failure
Source-level finding (AI Generated Content)
Pinned source references:
MidiSessionTracker.h:m_sessionsis astd::vector<MidiSessionEntry>; each entry stores aContextHandle.AddClientSession, lines 207-219: pushes the entry, obtains its address, returns that address as the context handle, and stores it in the entry.RemoveClientSession, lines 325-330: erases the entry, moving subsequent vector elements while retaining their saved context values.FindSessionForContextHandle, lines 58-66: returns the first entry whose saved context equals the supplied token.RemoveClientSessionInternal, lines 360-378: looks up that token, destroys the selected session's client connections, and erases the session.MidiSrvRpc.cpp:PMIDISRV_CONTEXT_HANDLE_rundownpasses the context toRemoveClientSessionInternal; ordinary deregistration callsRemoveClientSessionby session GUID and process ID.
Steps to reproduce the identity collision
This is a standalone model of the container/identity operations, not a call into the actual tracker, MIDI APIs, or RPC runtime.
- Add sessions A and B, with context tokens taken from their vector-entry addresses.
- Remove A normally. B moves to an earlier vector slot but retains its original context token.
- Add C. It occupies B's former slot and receives the same token B still holds.
- Look up C's token using the tracker's first-match lookup. B is selected instead of C.
Expected: a cleanup token identifies only its owning live session; cleaning up C cannot select B.
Observed in the model: B and C have equal tokens, and cleanup using C's token selects/removes B. This is sequential, so locking alone would not remove the collision. Reserved capacity excludes reallocation as a necessary cause.
Self-contained C++17 model (AI Generated Content)
#include <algorithm>
#include <iostream>
#include <vector>
// Models the address/token operations still present at
// microsoft/MIDI 4c8444661b1cc6ee7c88f2a083459baf6bf8508d.
// No MIDI APIs, services, or application processes are touched.
struct Session
{
int id;
void* context = nullptr;
};
int main()
{
std::vector<Session> sessions;
sessions.reserve(8); // Exclude reallocation: erase alone is sufficient.
const auto add = [&](int id)
{
sessions.push_back({id});
auto& entry = sessions.back();
entry.context = &entry;
return entry.context;
};
add(1);
void* longLivedContext = add(2);
sessions.erase(sessions.begin());
void* newClientContext = add(3);
const bool collision = longLivedContext == newClientContext;
std::cout << "Distinct live sessions share a context token: " << collision << '\n';
const auto victim = std::find_if(sessions.begin(), sessions.end(),
[&](const Session& s) { return s.context == newClientContext; });
if (victim == sessions.end())
return 2;
const int removed = victim->id;
sessions.erase(victim);
std::cout << "Cleanup for session 3 removed session: " << removed << '\n';
std::cout << "Remaining session: " << sessions.front().id << '\n';
std::cout << "Model only; not a reproduction against the installed Windows service.\n";
return collision && removed == 2 && sessions.front().id == 3 ? 0 : 1;
}
In an MSVC Developer Command Prompt:
cl /nologo /EHsc /std:c++17 main.cpp /Fe:session-model.exe
session-model.exe
Output (exit code 0):
Distinct live sessions share a context token: 1
Cleanup for session 3 removed session: 2
Remaining session: 3
Model only; not a reproduction against the installed Windows service.
Scope and remaining validation
- This demonstrates duplicate identity in the modeled source operations. It does not reproduce actual service registration/rundown, validate RPC behavior with duplicate context values, or establish which Windows binaries contain this code.
- An upstream regression test against
CMidiSessionTracker, followed by a real RPC lifecycle test, would be stronger validation. Neither has been performed here. - The original application incident prompted the investigation, but the causal connection remains unconfirmed. In particular, the service session list was not captured during that failure.
- Existing issue searches for session/context and session/pointer reports found #564 and other runtime reports, but no separate report of this specific vector-erase/slot-reuse mechanism. This issue is filed separately as requested, not asserted to be the same defect as #564.
Environment / diagnostics
- Windows at the original incident: Windows 11 25H2, build 26200.9168. This is background context, not an assertion that the installed service matches the inspected source.
- Standalone model: C++17, MSVC 19.51.36256.0; no MIDI hardware or SDK required.
- Type/location: source-level session-tracker identity issue in the in-box MIDI service.
- Application/device/vendor driver: not required for the standalone model. The original runtime incident involved a custom JUCE/WinMM app and an S88 mk2; it is not used as proof of the source mechanism.
- MIDI Diagnostics Tool output: not available for the original incident. No new diagnostic-tool collection or real-service reproduction was performed for this source-focused report.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.