Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to cast SOCKET to int under Win64?

I’m working on a Windows port of a POSIX C++ program.

The problem is that standard POSIX functions like accept() or bind() expect an ‘int’ as the first parameter while its WinSock counterparts use ‘SOCKET’.
When compiled for 32-bit everything is fine, because both are 32bit, but under Win64 SOCKET is 64 bit and int remains 32 bit and it generates a lot of compiler warning like this:

warning C4244: '=' : conversion from 'SOCKET' to 'int', possible loss of data

I tried to work around the issue by using a typedef:


#ifdef _WIN32
 typedef SOCKET sock_t;
#else
 typedef int sock_t;
#endif

and replacing ‘int’s with sock_t at the appropriate places.

This was fine until I reached a part of the code which calls OpenSSL APIs.
As it turned out OpenSSL uses ints for sockets even on Win64. That seemed really strange, so I started searching for an answer, but the only thing I found was an old post on the openssl-dev mailing list which refered to a comment e_os.h:


/*
 * Even though sizeof(SOCKET) is 8, it's safe to cast it to int, because
 * the value constitutes an index in per-process table of limited size
 * and not a real pointer.
 */

So my question is:
is it really safe to cast SOCKET to int?

I’d like to see some kind of documentation which proves that values for SOCKET can't be larger than 2^32.

Thanks in advance!
Ryck

like image 749
Ryck Avatar asked Dec 23 '09 16:12

Ryck


2 Answers

This post seems by the to be repeating the information on kernel objects at msdn:

Kernel object handles are process specific. That is, a process must either create the object or open an existing object to obtain a kernel object handle. The per-process limit on kernel handles is 2^24.

The thread goes on to cite Windows Internals by Russinovich and Solomon as a source for the high bits being zero.

like image 109
Pete Kirkham Avatar answered Nov 20 '22 00:11

Pete Kirkham


The simple answer to this question is no. Take a look at the description of SOCKET values on MSDN [1]:

Windows Sockets handles have no restrictions, other than that the value INVALID_SOCKET is not a valid socket. Socket handles may take any value in the range 0 to INVALID_SOCKET–1.

So clearly, the API allows all values in the range [0, 2^64 - 1) on 64-bit Windows. And if the API ever returned a value greater than 2^32 - 1, assigning it to an int would result in handle truncation. Also have a look at the description of the return value from the socket() function [2]:

If no error occurs, socket returns a descriptor referencing the new socket.

Notice that it most emphatically does not promise to return a kernel handle. This makes any discussion about the possible values for kernel handles moot.

All that being said, as of this writing, the socket() function really does return a kernel handle (or something indistinguishable from a kernel handle) [3] and kernel handles really are limited to 32-bits [4]. But keep in mind that Microsoft could change any of these things tomorrow without breaking their interface contracts.

However, since a doubtless large number of applications have taken a dependency on these particular implementation details (and more importantly, so has OpenSSL), Microsoft would likely think twice about making any breaking changes. So go ahead and cast a SOCKET to an int. Just keep in mind that this an inherently dangerous, bad practice, and is never justifiable in the name of expediency.

  1. http://msdn.microsoft.com/en-us/library/windows/desktop/ms740516(v=vs.85).aspx
  2. http://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx
  3. http://msdn.microsoft.com/en-us/library/windows/desktop/ms742295(v=vs.85).aspx
  4. http://msdn.microsoft.com/en-us/library/windows/desktop/aa384267(v=vs.85).aspx

Edit (2018-01-29)

Since this topic still seems to be of some interest, it's worth pointing out that it's quite easy to write portable sockets code in C++11 without resorting to questionable type casts:

using socket_t = decltype(socket(0, 0, 0));

socket_t s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
like image 24
Peter Ruderman Avatar answered Nov 20 '22 00:11

Peter Ruderman