-
Notifications
You must be signed in to change notification settings - Fork 5.1k
[Android] Prevent race condition by synchronizing buffer access #117950
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
base: main
Are you sure you want to change the base?
[Android] Prevent race condition by synchronizing buffer access #117950
Conversation
/azp run runtime-android,runtime-androidemulator |
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command. |
/azp run runtime-android,runtime-androidemulator |
Azure Pipelines successfully started running 2 pipeline(s). |
Tagging subscribers to 'arch-android': @vitek-karas, @simonrozsival, @steveisok, @akoeplinger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes a race condition in Android SSL implementation where GCHandle
references were being freed while native callbacks could still access them, causing InvalidCastException
or NullReferenceException
. The fix extends the GCHandle
lifetime to match the SafeDeleteSslContext
object and adds proper synchronization for buffer operations.
Key changes:
- Replace weak
GCHandle
allocation with persistent handle that lives for the full object lifetime - Add comprehensive locking around all buffer operations to prevent race conditions
- Implement null/disposed checks in native callbacks with graceful error handling
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
File | Description |
---|---|
src/libraries/tests.proj | Re-enables System.Net.Http.Functional.Tests for Android and adds it to smoke tests |
src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs | Implements persistent GCHandle, thread-safe buffer access, and defensive callback handling |
src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs
Outdated
Show resolved
Hide resolved
…Android/SafeDeleteSslContext.cs Co-authored-by: Jan Kotas <[email protected]>
/azp run runtime-android,runtime-androidemulator |
Azure Pipelines successfully started running 2 pipeline(s). |
src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs
Outdated
Show resolved
Hide resolved
/azp run runtime-android,runtime-androidemulator |
Azure Pipelines successfully started running 2 pipeline(s). |
/azp run runtime-android,runtime-androidemulator |
Azure Pipelines successfully started running 2 pipeline(s). |
/azp run runtime-android,runtime-androidemulator |
Azure Pipelines successfully started running 2 pipeline(s). |
/azp run runtime-android,runtime-androidemulator |
Azure Pipelines successfully started running 2 pipeline(s). |
catch (Exception ex) | ||
{ | ||
Debug.Write("Exception Caught. - " + ex); | ||
throw; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exceptions thrown from UnmanagedCallersOnly
methods turn into unhandled exceptions (on non-Windows). This means that the app is going to crash if any exceptions are thrown by this method. Is it what we want? Should the exceptions be propagated to the user instead?
If you want to keep the behavior and crash the app if any exception is thrown here, it is better to do that without the try/catch. It is better for diagnostics. try/catch/rethrow can overwrite some of the details that may be useful for diagnosing the exceptions, and the unhandled exception handler should print the same details as the Debug.Write to the console.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the context. I agree -- it should be propagated to the user.
if (handle.IsAllocated) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (handle.IsAllocated) | |
{ |
This is unnecessary. Non-null managedContextHandle
is always going to turn into Allocated GCHandle (even if the handle was freed somewhere else already).
GCHandle is struct that is as dangerous as unsafe code. If you call Free on one copy of the struct, the other copies will still report IsAllocated==true even through the handle was freed.
GCHandle h = GCHandle.FromIntPtr(connection); | ||
Debug.Assert(h.IsAllocated); | ||
|
||
SafeDeleteSslContext? context = (SafeDeleteSslContext?)h.Target; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can consider switching to new GCHandle<T>
to avoid this cast
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Actually, WeakGCHandle<T>
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, updated. TryGetTarget
handles both invalid GCHandle and null context.
…nection methods to use WeakGCHandle
/azp run runtime-android,runtime-androidemulator |
Azure Pipelines successfully started running 2 pipeline(s). |
Problem
Android SSL callbacks
WriteToConnection
/ReadFromConnection
receive an IntPtr that should reference a GCHandle toSafeDeleteSslContext
. The handle was freed while native code could still invoke the callbacks with the IntPtr referencing a different object. This led to a NullReferenceException or InvalidCastException:Description
This PR synchronizes all buffer mutations behind a shared
_lock
, and updates the managed callbacks to verify that the handle is still allocated and its target is aSafeDeleteSslContext
before using it.Changes
Testing
The
System.Net.Http.Functional.Tests
now pass without the crash on Android.Fixes #117045