4

There is a mismatch between the .NET Framework version and the native Win32 version of the WSAData struct, for 64-bit apps, because the order of the fields is different. I had copied the .NET version for use in our C#-based product and a coworker is concerned that I have caused a memory corruption. Is there any risk of memory corruption because of this mismatch when using DllImport / PInvoke? Is there any risk of an invalid memory access when marshaling the native version to the managed version? Let's assume I'm not concerned with actually accessing the fields of the resulting WSAData object. I just want to be certain that my call to WSAStartup does not corrupt memory or crash the app.

Here is the native C++ version in WinSock2.h. Note that the order of the members is different in 64-bit versus 32-bit. WSADESCRIPTION_LEN is 256 and WSASYS_STATUS_LEN is 128.

typedef struct WSAData {
        WORD                    wVersion;
        WORD                    wHighVersion;
#ifdef _WIN64
        unsigned short          iMaxSockets;
        unsigned short          iMaxUdpDg;
        char FAR *              lpVendorInfo;
        char                    szDescription[WSADESCRIPTION_LEN+1];
        char                    szSystemStatus[WSASYS_STATUS_LEN+1];
#else
        char                    szDescription[WSADESCRIPTION_LEN+1];
        char                    szSystemStatus[WSASYS_STATUS_LEN+1];
        unsigned short          iMaxSockets;
        unsigned short          iMaxUdpDg;
        char FAR *              lpVendorInfo;
#endif
} WSADATA, FAR * LPWSADATA;

Here is the managed version in the .NET Framework:

[StructLayout(LayoutKind.Sequential)]
internal struct WSAData {
    internal short wVersion;
    internal short wHighVersion;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=257)]
    internal string szDescription;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=129)]
    internal string szSystemStatus;
    internal short iMaxSockets;
    internal short iMaxUdpDg;
    internal IntPtr lpVendorInfo;
}

[DllImport(WS2_32, CharSet=CharSet.Ansi, BestFitMapping=false,
    ThrowOnUnmappableChar=true, SetLastError=true)]
internal static extern SocketError WSAStartup(
    [In] short wVersionRequested,
    [Out] out WSAData lpWSAData
                                               );
MD XF
  • 7,860
  • 7
  • 40
  • 71
Ron
  • 1,888
  • 20
  • 25
  • Why are you calling `WSAStartup` yourself from .net? Just use [`System.Net.Sockets.Socket`](https://msdn.microsoft.com/en-us/library/System.Net.Sockets.Socket%28v=vs.110%29.aspx) which takes care of that for you. – Mgetz Mar 22 '15 at 19:25
  • The original motivation was to avoid throwing exceptions when our clients (programmers) were using our code with Visual Studio and break-on-exceptions was set. It's annoying to get exceptions just because the server couldn't be connected to. But I'm also concerned that there may be a bug in the .NET Framework. – Ron Mar 22 '15 at 19:29
  • 2
    Then `catch` the exceptions and tell your clients to enable **just my code** when debugging. The likely hood of their being a bug in the .NET framework source code by version 4.5 is very slim. If you find one [file it](https://github.com/dotnet/corefx) – Mgetz Mar 22 '15 at 19:32

2 Answers2

4

Sure, this is incorrect. The .NET Framework gets away with it, the structure sizes are still correct (400 bytes in 32-bit mode, 408 in 64-bit mode) so no memory corruption can occur. And it doesn't actually use any of the returned info, they would surely have caught the bug if they did.

You can file the bug at connect.microsoft.com, I doubt they'll be in a hurry to fix it however.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • at this point even though `System.Net.Sockets` isn't up there yet I'd file it on the [corefx repo](https://github.com/dotnet/corefx/issues). Much more likely to get dealt with. – Mgetz Mar 22 '15 at 19:48
  • It is not part of it. – Hans Passant Mar 22 '15 at 19:50
  • 1
    Interestingly, even the [MSDN](https://msdn.microsoft.com/en-us/library/windows/desktop/ms741563(v=vs.85).aspx) documentation on the data structure doesn't include this little fact of the difference between 32 and 64 bits. – xanatos Mar 22 '15 at 19:50
  • 1
    @HansPassant [it's listed](http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-10-58-94-19/NetCore_5F00_OpenSourceUpdate.xlsx) as being targeted for github release with corefx. So it will be at some point in theory. – Mgetz Mar 22 '15 at 19:52
1

Yes, the structure is wrong... You can test by using the wrong structure and the right structure:

[StructLayout(LayoutKind.Sequential)]
internal struct WSAData
{
    internal short wVersion;
    internal short wHighVersion;
    internal short iMaxSockets;
    internal short iMaxUdpDg;
    internal IntPtr lpVendorInfo;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
    internal string szDescription;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)]
    internal string szSystemStatus;
}

WSAData data;
int res = WSAStartup(2 << 8 | 2, out data);
Console.WriteLine("64bits: {0}, Result = {1}, szDescription: {2}, szSystemStatus: {3}", Environment.Is64BitProcess, res, data.szDescription, data.szSystemStatus);

BUT you don't have any problem, because probably .NET doesn't use all these various fields (what does it needs the description for?) There can't even be a problem with Unicode conversion, because the method used is the Ansi one (CharSet=CharSet.Ansi), and every Ansi character is a legal character in Unicode.

xanatos
  • 109,618
  • 12
  • 197
  • 280