develooper Front page | perl.perl5.porters | Postings from September 2013

[perl #118059] race condition+fail in dist\IO\t\cachepropagate-tcp.t

Thread Previous
From:
bulk88 via RT
Date:
September 30, 2013 00:04
Subject:
[perl #118059] race condition+fail in dist\IO\t\cachepropagate-tcp.t
Message ID:
rt-3.6.HEAD-31239-1380499481-1248.118059-15-0@perl.org
Adding some things I said in IRC for archival reasons.

Would this make any sense, in Winsock, if a process does a loopback
connection, the Winsock creates a pipe OS handle instead of a packetized
TCP OS handle, then registers the pipe OS handle with a Winsock user
mode struct so all socket funcs will work as normal on the pipe handle,
then the socket() returns the same handle that accept() returns, and
instead of going through the OS packet switcher, they talk over a pipe
which is more efficient? About the bug, what happens is, the accept()
calls into kernel mode and the server is descheduled from the CPU, then
the child psuedo fork runs, does its connect(), then closesocket()s
during perl ithread glboal destruction, then the accept() unblocks from
kernel mode, but the handle is that unblocked the accept is dead already?

Invalid handle exception with C debugger.
______________________________
 	ntdll.dll!_ZwClose@4()  + 0x12 bytes	
 	mswsock.dll!_SockCloseSocket@4()  + 0x240 bytes	
 	mswsock.dll!_WSPAccept@24()  + 0xaef bytes	
 	ws2_32.dll!_WSAAccept@20()  + 0x85 bytes	
 	ws2_32.dll!_accept@12()  + 0x17 bytes	
>	perl519.dll!win32_accept(unsigned int s=3, sockaddr * addr=0x0012f700,
int * addrlen=0x0012fb00)  Line 370 + 0x12 bytes	C
 	perl519.dll!PerlSockAccept(IPerlSock * piPerl=0x01b67218, unsigned int
s=3, sockaddr * addr=0x0012f700, int * addrlen=0x0012fb00)  Line 1227 +
0x11 bytes	C++
 	perl519.dll!Perl_pp_accept(interpreter * my_perl=0x01b64e0c)  Line
2557 + 0x37 bytes	C
 	perl519.dll!Perl_runops_standard(interpreter * my_perl=0x01b64e0c) 
Line 42 + 0xc bytes	C
 	perl519.dll!S_run_body(interpreter * my_perl=0x01b64e0c, long
oldscope=1)  Line 2500 + 0xf bytes	C
 	perl519.dll!perl_run(interpreter * my_perl=0x01b64e0c)  Line 2419	C
 	perl519.dll!RunPerl(int argc=2, char * * argv=0x01b64d68, char * *
env=0x01b63430)  Line 270 + 0x9 bytes	C++
 	perl.exe!__tmainCRTStartup()  Line 582 + 0x17 bytes	C
 	kernel32.dll!_BaseProcessStart@4()  + 0x28 bytes	
______________________________

The invalid handle passed to NtClose in the parent thread is the same OS
kernel handle that appeared on the CRT level from $connector.
_________________________________
	my $connector = IO::Socket::INET->new(PeerAddr => '127.0.0.1',
					      PeerPort => $port,
					      Proto => 'tcp');
	Win32::API::WriteMemory($ptr, pack('I',
Win32API::File::GetOsFHandle(*{$connector}{IO})), 4);
_________________________________
So Winsock somehow lets accept() and its callees in parent thread see
the OS handle of the child thread and try to close it from the parent
thread.

The pointer memory write stuff was the fastest way I though of for the
child thread to report its data without doing stdio to console which
then stops the race/bug.

The accept actually fails with 10038 WSAENOTSOCK but PerlIO corrupts it
to ERROR_INVALID_HANDLE/6. Disassembly of WSPAccept and more debugging
shows the 10038 is coming from a NtDeviceIoControlFile call in WSPAccept
with IOCTL 0x12010 which google and reactos say is a constant called
AFD_ACCEPT. NtDeviceIoControlFile AFD_ACCEPT returns 0xc0000008 
STATUS_INVALID_HANDLE . The asm shows SockCloseSocket branch is only
taken on error codes != 0, so its resource cleanup code on failure.
NtStatusToSocketError later in WSPAccept converts STATUS_INVALID_HANDLE
to WSAENOTSOCK which becomes the public facing error code of the failed
accept().

Because of the is a loopback in the same process implemented using pipes
by winsock theory I present above. TonyC asked are the handles the same
from the accept() in the parent thread and connect() in the child
thread? On a successful accept(), the OS handle returned is DIFFERENT
from the OS handle in $connector in the child. Not in IRC comment: I
didnt research if the 2 handles are the same kernel object/struct in
object manager and Winsock is doing a DuplicateHandle internally.

This is an answer for another TonyC question. The fds in a successful
accept() are different also. There is a class of bugs/limitations in
Win32 psuedo-fork where the fileno/fds aren't virtualized between the
psuedo-procs. Child socket is fd 4 and parent socket is fd 5 in perl
lang on a successful accept() by my tests. See earlier posts in this ticket.

---
via perlbug:  queue: perl5 status: open
https://rt.perl.org:443/rt3/Ticket/Display.html?id=118059

Thread Previous


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About