This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
win32/win32sck.c: dont close() a freed socket os handle
authorDaniel Dragan <bulk88@hotmail.com>
Sat, 2 Nov 2013 03:04:35 +0000 (23:04 -0400)
committerSteve Hay <steve.m.hay@googlemail.com>
Sat, 2 Nov 2013 19:35:43 +0000 (19:35 +0000)
commitb47a847f6284f6f98ad7509cf77a4aeb802d8fce
tree418e95b955a5704fce4f1f4abfb5a4b6bfa76a38
parent50e4f4d46de7747365d201feabf76ce56822c514
win32/win32sck.c: dont close() a freed socket os handle

This patch is in RT as [perl #120091] but also fixes [perl #118059].

Because the MS C lib, doesn't support sockets natively, Perl uses
open_osfhandle, to wrap a socket into CRT fd. Sockets handles must be
closed with closesocket, not CloseHandle (which CRT close calls).
Undefined behavior will happen otherwise according to MS. Swap the now
closed socket handle in the CRT to INVALID_HANDLE_VALUE. The CRT will
not call CloseHandle on INVALID_HANDLE_VALUE and returns success if it
sees INVALID_HANDLE_VALUE. CloseHandle will never be called on a socket
handle with this patch.

In #118059, a race condition was reported, where accept() failed with
ENOTSOCK, when a psuedofork was done, and connection from the child fake
perl process to parent fake perl process was done. The race condition's
effects occur inside the user mode code in mswsock.dll!_WSPAccept in the
parent perl, which winds up having a kernel handle of an unknown type
in it that is invalid. The invalid handle is passed to kernel calls, which
fail, because the handle is invalid, and the error is translated to
ENOTSOCK.  The exact mechanism of the bug and 100% reproducabilty of the
race were never established, since mswsock.dll is closed source.

Another benefit of this patch is making it easier to use C debuggers on
a Win32 Perl because of less debugger-only bad handle exceptions
(NtGlobalFlag FLG_ENABLE_HANDLE_EXCEPTIONS/0xC0000008 STATUS_INVALID_HANDLE).

This commit reverts parts of commit 9b1f18150a
"Get rid of PERL_MSVCRT_READFIX" and parts of commit 46e77f1118
"Don't use the PERL_MSVCRT_READFIX when using VC++ 7.x onwards." and
contains additional changes not found in those 2 commits. The method for
selecting the definition of struct ioinfo isn't perfect. It will break if
VC > 6 is changed to use the older msvcrt.dll. For some versions of the
CRT, like 2005/8.0, it is impossible to know the definition of ioinfo
struct at C compile time, since the struct increased in size a number of
times with higher build numbers of v8.0 CRT. SxS and security updates make
that same perl binary will run with different v8.0 CRTs at different times.
For the case when ioinfo can not be hard coded, introduce
WIN32_DYN_IOINFO_SIZE. With WIN32_DYN_IOINFO_SIZE, the size of the ioinfo
struct is determined on Perl process startup using _mize. Since VC 2013
is a brand new product at the time of this patch, even though its struct
is known, and 2008 through 2012 have been stable so far, don't assume at
this time 2013's ioinfo will be stable. Therefore, VC 2003 and older
(including Mingw's v6.0), 2008 to 2012, are hard coded. 2013 is a candidate
one day to be hard coded. VC 2005 can never be hard coded.

All non-WIN32_DYN_IOINFO_SIZE compilers will work with
WIN32_DYN_IOINFO_SIZE. Non-WIN32_DYN_IOINFO_SIZE mode is simply more
efficient.

For future compatibility concerns, 3 forms of protection are offered.
If __pioinfo isn't exported anymore, the Perl build will break. In
WIN32_DYN_IOINFO_SIZE mode, if __pioinfo isn't heap memory anymore or the
start of a memory block, the runtime panic will happen. In with and without
WIN32_DYN_IOINFO_SIZE, only on a DEBUGGING build, the handle returned by
CRT's _get_osfhandle, which is the authentic handle in the CRT, is compared
to the handle found by accessing the ioinfo struct directly. If they don't
match, the handle swapping code is broken and the assert fails.
win32/win32.c
win32/win32.h
win32/win32sck.c