Skip to content

Application.ThreadContext.FromId reads from s_contextHash while another thread may be writing #13246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
KalleOlaviNiemitalo opened this issue Apr 6, 2025 · 7 comments
Assignees
Labels
💥 regression-release Regression from a public release
Milestone

Comments

@KalleOlaviNiemitalo
Copy link

.NET version

9.0.3

Did it work in .NET Framework?

Yes

Did it work in any of the earlier releases of .NET Core or .NET 5+?

I guess it would have worked in .NET 7, because #8161 was not merged there.

Issue description

The Application.ThreadContext.FromId method reads from the s_contextHash dictionary without locking. This can cause an error if another thread writes to the dictionary at the same time.

internal static ThreadContext? FromId(uint id)
{
if (!s_contextHash.TryGetValue(id, out ThreadContext? context) && id == PInvoke.GetCurrentThreadId())
{
context = Create();
}
return context;
}

Writers do lock (s_tcInternalSyncObject) but this reader doesn't. AFAICT, the callers of Application.ThreadContext.FromId don't lock it either.

Before #8161, s_contextHash used to be a Hashtable, which is safe for one writer in parallel with multiple readers. Dictionary<TKey, TValue> is not safe for that use.

Steps to reproduce

Found by source code inspection, not reproduced.

@KalleOlaviNiemitalo KalleOlaviNiemitalo added the untriaged The team needs to look at this issue in the next triage label Apr 6, 2025
@Tanya-Solyanik Tanya-Solyanik added the 💥 regression-release Regression from a public release label Apr 8, 2025
@Tanya-Solyanik
Copy link
Member

Tanya-Solyanik commented Apr 8, 2025

@Olina-Zhang - could your team please work on a unit test or a ScratchProject repro that exercises the relevant code for this issue. Please share your private branch when done.

@Olina-Zhang
Copy link
Member

@Tanya-Solyanik Tried to repro with that relevant code from the Application.ThreadContext.FromId method, but cannot, no any error after simulating a race condition between a reader and a writer.
here is my sample app: TestSample.zip
@KalleOlaviNiemitalo Can you give us some support how to reproduce this issue? Thanks!

@KalleOlaviNiemitalo
Copy link
Author

@Olina-Zhang why does your sample app have private static readonly ConcurrentDictionary<uint, ThreadContext> s_contextHash?

On the main branch, s_contextHash is a Dictionary<uint, ThreadContext> rather than a ConcurrentDictionary<uint, ThreadContext>:

private static readonly Dictionary<uint, ThreadContext> s_contextHash = [];

Likewise in v9.0.3:

private static readonly Dictionary<uint, ThreadContext> s_contextHash = [];

@KalleOlaviNiemitalo
Copy link
Author

I mean, ConcurrentDictionary may be a good way to fix the bug, but you cannot reproduce the error if you use that in the sample app.

@merriemcgaw merriemcgaw removed the untriaged The team needs to look at this issue in the next triage label Apr 8, 2025
@merriemcgaw merriemcgaw added this to the 10.0 Preview4 milestone Apr 8, 2025
@JeremyKuhne
Copy link
Member

@KalleOlaviNiemitalo are you hitting this? Are you willing to propose a fix PR? If not, I'll look into fixing this when I've got a few cycles.

@Olina-Zhang don't worry about a repro here.

@KalleOlaviNiemitalo
Copy link
Author

No I am not hitting this. And won't make a PR.

@JeremyKuhne
Copy link
Member

No I am not hitting this. And won't make a PR.

Thanks for the clarification. I'll take care of it when I can.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💥 regression-release Regression from a public release
Projects
None yet
Development

No branches or pull requests

5 participants