Bug 47425 - System.Net.Sockets.Socket.DuplicateAndClose does not correctly remove its own reference
Summary: System.Net.Sockets.Socket.DuplicateAndClose does not correctly remove its own...
Status: RESOLVED FIXED
Alias: None
Product: Class Libraries
Classification: Mono
Component: System ()
Version: 4.8.0 (C9)
Hardware: PC Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2016-11-18 21:31 UTC by Kenneth
Modified: 2016-12-29 20:02 UTC (History)
1 user (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Reproducible test-case (2.45 KB, application/octet-stream)
2016-11-18 21:31 UTC, Kenneth
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Kenneth 2016-11-18 21:31:04 UTC
Created attachment 18549 [details]
Reproducible test-case

When calling "DuplicateAndClose" on a socket, it creates a SocketInfo descriptor, which can then be used to re-create the socket (ideally in another AppDomain).

Unfortunately, this does not work, because the code just sets the safe_handle to null:
https://github.com/mono/mono/blob/master/mcs/class/System/System.Net.Sockets/Socket.cs#L2299

This means that the safe-handle is no longer referenced and will be collected by the next GC. Once this happens it will close the socket it references, and any access to the socket will give System.Net.Sockets.SocketException("The descriptor is not a socket").

I have attached a reproducible test case that reproduces the issue on OSX and Linux.

Simply doing this will trigger the issue:

socket = new Socket(socket.DuplicateAndClose(pid));
GC.Collect();

Next access to socket will give "The descriptor is not a socket":
socket.Send(new byte[] { 0 });

In the example I also show that pulling out the safe_handle field and calling GC.SuppressFinalize on it will mitigate the problem, but the system appears to deadlock later, presumably because it runs out of handles.
I have not figured out a workaround for this problem, but a possible fix could be to transfer the safe_handle in the SocketInfo instance so it can be properly disposed with the re-created socket. This does not work cross-process, but that is already a limitation for DuplicateAndClose.
Comment 1 Kenneth 2016-11-18 21:57:00 UTC
The "deadlock" I mentioned happens because the is no check on the client and it crashes intermittently with the message "Address already in use", which is another issue and deserves its own bug report :).

tl;dr; simply add GC.SuppressFinalize(safe_handle) here:
https://github.com/mono/mono/blob/master/mcs/class/System/System.Net.Sockets/Socket.cs#L2299

Manual workaround is using reflection to pull the safe_handle field and calling GC.SuppressFinalize() on the value.
Comment 2 Kenneth 2016-12-29 20:02:37 UTC
This has been fixed and merged:
https://github.com/mono/mono/pull/3995