Navigation Menu

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perl crash when run under AppVerifier #12986

Open
p5pRT opened this issue May 22, 2013 · 25 comments
Open

Perl crash when run under AppVerifier #12986

p5pRT opened this issue May 22, 2013 · 25 comments

Comments

@p5pRT
Copy link

p5pRT commented May 22, 2013

Migrated from rt.perl.org#118127 (status was 'open')

Searchable as RT118127$

@p5pRT
Copy link
Author

p5pRT commented May 22, 2013

From karthik.rajagopalan@schrodinger.com

Hi Folks,

We use perl widely for one of our job monitoring application. And recently
we received reports from customer that the application crash after few
hours with 'invalid handle' exception in ntdll.dll.
So we planned to run our application in Application Verifier of Windows to
trace handles and having a hard time to run perl under it. Currently the
following piece of code extracted in the form of a sample script crash
under AppVerifier -

use IO​::Socket​::INET;
use IPC​::Open2;
my $log = "txt";
open OUT, -f $log ? ">> $log" : ">$log";

# Pass an open handle of tempfile to child process.
open FILE, ">temp";
binmode FILE;
print FILE "abc";
close FILE;
open IN, "temp";

print "here\n";
my $child_pid = open2(">&OUT","<&IN","(notepad.exe)2>&1");
print "here1\n";
close IN;
close OUT;

Exactly after notepad.exe process is spawned we see the crash with
following stack trace. We see a similar trace at customer site when a
handle is being closed through closesocket(..) call which is not really a
socket handle. The stack trace given below is from perl-5.10.1. We also see
a similar stack trace with 5.14.2. So I am sure this is a problem even in
current running version of perl. Can you please take a look and respond
back?

-Karthik

0​:000> !analyze -v
*******************************************************************************
*
  *
* Exception Analysis
  *
*
  *
*******************************************************************************

*** WARNING​: Unable to verify checksum for
c​:\Schrodinger2012_x64\latest\mmshare-v2.1\bin\Windows-x64\perl510.dll
*** WARNING​: Unable to verify checksum for
c​:\Schrodinger2012_x64\latest\mmshare-v2.1\bin\Windows-x64\perl.exe
APPLICATION_VERIFIER_HANDLES_INVALID_HANDLE (300)
Invalid handle exception for current stack trace.
This stop is generated if the function on the top of the stack passed an
invalid handle to system routines. Usually a simple kb command will reveal
what is the value of the handle passed (must be one of the parameters -
usually the first one). If the value is null then this is clearly wrong.
If the value looks ok you need to use !htrace debugger extension to get a
history of operations pertaining to this handle value. In most cases it
must be that the handle value is used after being closed.
Arguments​:
Arg1​: 00000000c0000008, Exception code.
Arg2​: 000000000108eaa0, Exception record. Use .exr to display it.
Arg3​: 000000000108e470, Context record. Use .cxr to display it.
Arg4​: 0000000000000000, Not used.

FAULTING_IP​:
vrfcore!VerifierStopMessageEx+779
000007fe`f76637ed cc int 3

EXCEPTION_RECORD​: 000000000108eaa0 -- (.exr 0x108eaa0)
ExceptionAddress​: 0000000077c5fec7
(ntdll!KiRaiseUserExceptionDispatcher+0x000000000000003a)
  ExceptionCode​: c0000008 (Invalid handle)
  ExceptionFlags​: 00000000
NumberParameters​: 0
Thread tried to close a handle that was invalid or illegal to close

FAULTING_THREAD​: 0000000000000ae8

DEFAULT_BUCKET_ID​: STATUS_BREAKPOINT

PROCESS_NAME​: perl.exe

CONTEXT​: 000000000108e470 -- (.cxr 0x108e470)
rax=0000000031c7226f rbx=0000000000000000 rcx=000000000108e470
rdx=000007fef5663da5 rsi=0000000000000000 rdi=0000000000000003
rip=0000000077c5fec7 rsp=000000000108ea80 rbp=000000000108ebd0
r8=000000000108eb48 r9=000000000108ebd0 r10=0000000000000000
r11=0000000000000202 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000012043
iopl=0 nv up ei pl nz na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b
efl=00000206
ntdll!KiRaiseUserExceptionDispatcher+0x3a​:
00000000`77c5fec7 8b8424c0000000 mov eax,dword ptr [rsp+0C0h]
ss​:00000000`0108eb40=c0000008
Resetting default scope

BAD_HANDLE​: 0000000000000003 (!htrace 0000000000000003)

ERROR_CODE​: (NTSTATUS) 0x80000003 - {EXCEPTION} Breakpoint A breakpoint
has been reached.

EXCEPTION_CODE​: (HRESULT) 0x80000003 (2147483651) - One or more arguments
are invalid

EXCEPTION_PARAMETER1​: 0000000000000000

MOD_LIST​: <ANALYSIS/>

NTGLOBALFLAG​: 100

APPLICATION_VERIFIER_FLAGS​: 80000004

PRIMARY_PROBLEM_CLASS​: STATUS_BREAKPOINT

BUGCHECK_STR​: APPLICATION_FAULT_STATUS_BREAKPOINT

LAST_CONTROL_TRANSFER​: from 000007fef56578dd to 000007fef76637ed

STACK_TEXT​:
00000000`0108d970 000007fe`f56578dd : 00000000`0108dd10 00000000`77d48350
00000030`00000004 00000001`002b0000 : vrfcore!VerifierStopMessageEx+0x779
00000000`0108dca0 00000000`77c28a8f : 00000000`028f0dc0 00000000`eceeae86
00000000`77d48350 00000000`0618ccf9 :
vfbasics!AVrfpVectoredExceptionHandler+0x85
00000000`0108dcf0 00000000`77c259b2 : 00000000`0000000e 00000000`00000000
00000000`00000080 00000000`028f0db0 : ntdll!RtlpCallVectoredHandlers+0xa8
00000000`0108dd60 00000000`77c262ee : 00000000`00000000 00000000`00000000
00000000`00000001 000007fe`00000015 : ntdll!RtlDispatchException+0x22
00000000`0108e440 00000000`77c5fec7 : 00000000`00000000 00000000`00000000
00000000`00000003 00000000`00033283 : ntdll!RtlRaiseException+0x221
00000000`0108ea80 000007fe`f5663da5 : 00000000`00000003 00000000`0108ec18
00000000`0108ec20 00000000`0108ec28 :
ntdll!KiRaiseUserExceptionDispatcher+0x3a
00000000`0108eb50 000007fe`fd3c80d8 : 00000000`00000000 00000000`00000370
00000000`00000000 00000000`00000000 :
vfbasics!AVrfpNtDeviceIoControlFile+0x171
00000000`0108ec10 000007fe`fd3abfe0 : 00000000`00000003 00000000`05941380
00000000`00000003 00000000`00000001 : mswsock!SockImportHandle+0x108
00000000`0108ee00 000007fe`fd3cbd19 : 00000000`00000003 00000000`05941380
00000000`00000006 00000000`02b57bf8 : mswsock!_GSHandlerCheck_SEH+0x408a
00000000`0108ee30 000007fe`fe797b7e : 00000000`00000003 000007fe`0000ffff
00000000`00000003 00000000`05941380 : mswsock!WSPGetSockOpt+0x99
00000000`0108ef10 000007fe`fe7ad71a : 00000000`05945590 00000000`00000000
00000000`05941380 00000000`02b50000 : WS2_32!DPROVIDER​::WSPGetSockOpt+0x3e
00000000`0108ef50 000007fe`fe7ad7f0 : 00000000`00000208 00000000`00003c30
00000000`00000000 000007fe`fe781ac0 :
WS2_32!DCATALOG​::FindIFSProviderForSocket+0xca
00000000`0108f260 000007fe`fe7903fd : 00000000`00000001 00000000`00000000
00000000`00000000 000007fe`f565a64d : WS2_32!DSOCKET​::FindIFSSocket+0x40
00000000`0108f290 00000000`6b8e4756 : 00000000`00000000 00000000`00000000
00000000`02b57bf8 00000000`00000001 : WS2_32!_chkstk+0x3ecb
00000000`0108f2e0 00000000`6b8f70da : 00000000`00000000 00000000`06107358
00000000`02b57bf8 00000000`040015b0 : perl510!my_close+0x36
[c​:\perl\perl_with_fix-5.10.1\win32\win32sck.c @​ 470]
00000000`0108f310 00000000`6b8f2a51 : 00000000`06107358 00000000`02b57bf8
00000000`040015b0 00000000`00000001 : perl510!PerlIOUnix_close+0x5a
[c​:\perl\perl_with_fix-5.10.1\perlio.c @​ 2751]
00000000`0108f340 00000000`6b8f3257 : 00000000`040015b0 00000000`00000008
00000000`0108f4e0 00000000`6bdd8bc0 : perl510!PerlIOBase_close+0x91
[c​:\perl\perl_with_fix-5.10.1\perlio.c @​ 2181]
00000000`0108f370 00000000`6b8f37c7 : 00000000`00000008 00000000`040015b0
00000000`00000000 00000000`00000000 : perl510!PerlIOBuf_close+0x17
[c​:\perl\perl_with_fix-5.10.1\perlio.c @​ 4088]
00000000`0108f3a0 00000000`6b9e4256 : 00000000`00000008 00000000`00000002
00000000`0108f4e0 00000000`00000008 : perl510!Perl_PerlIO_close+0x37
[c​:\perl\perl_with_fix-5.10.1\perlio.c @​ 1432]
00000000`0108f3e0 00000000`6b96e84e : 00000000`04066440 00000000`6b980b3c
00000000`0615ec01 00000000`02b57bf8 : perl510!Perl_do_openn+0x1286
[c​:\perl\perl_with_fix-5.10.1\doio.c @​ 660]
00000000`0108f610 00000000`6b9e7f9c : 00000000`00000013 00000000`00000000
00000000`00000000 00000000`02b57bf8 : perl510!Perl_pp_open+0x27e
[c​:\perl\perl_with_fix-5.10.1\pp_sys.c @​ 561]
00000000`0108f690 00000000`6b9a8c06 : 00000000`02b57bf8 00000000`03fb9ee0
00000000`00000001 00000000`03ec6660 : perl510!Perl_runops_standard+0x16c
[c​:\perl\perl_with_fix-5.10.1\run.c @​ 40]
00000000`0108f700 00000000`6b9a8e94 : 00000000`02b57bf8 00000000`00000000
00000000`0108f6e0 00000000`03fb9ee0 : perl510!S_run_body+0x116
[c​:\perl\perl_with_fix-5.10.1\perl.c @​ 2433]
00000000`0108f730 00000000`6b8fd324 : 00000000`03fb39a0 00000000`03fba5a0
00000000`03fb9ee0 00000000`00000000 : perl510!perl_run+0x264
[c​:\perl\perl_with_fix-5.10.1\perl.c @​ 2352]
00000000`0108f8a0 00000001`3f0411b2 : 00000000`00000001 00000000`00000000
00000000`00000000 00000000`00000000 : perl510!RunPerl+0x124
[c​:\perl\perl_with_fix-5.10.1\win32\perllib.c @​ 270]
00000000`0108fcf0 00000000`77b0f56d : 00000000`00000000 00000000`00000000
00000000`00000000 00000000`00000000 : perl!__tmainCRTStartup+0x11a
[f​:\dd\vctools\crt_bld\self_64_amd64\crt\src\crtexe.c @​ 555]
00000000`0108fd20 00000000`77c43281 : 00000000`00000000 00000000`00000000
00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
00000000`0108fd50 00000000`00000000 : 00000000`00000000 00000000`00000000
00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d

FOLLOWUP_IP​:
mswsock!SockImportHandle+108
000007fe`fd3c80d8 448bf8 mov r15d,eax

SYMBOL_STACK_INDEX​: 7

SYMBOL_NAME​: mswsock!SockImportHandle+108

FOLLOWUP_NAME​: MachineOwner

MODULE_NAME​: mswsock

IMAGE_NAME​: mswsock.dll

DEBUG_FLR_IMAGE_TIMESTAMP​: 4a5bdfc4

STACK_COMMAND​: ~0s ; kb

FAILURE_BUCKET_ID​: STATUS_BREAKPOINT_80000003_mswsock.dll!SockImportHandle

BUCKET_ID​:
X64_APPLICATION_FAULT_STATUS_BREAKPOINT_mswsock!SockImportHandle+108

Followup​: MachineOwner

@p5pRT
Copy link
Author

p5pRT commented May 23, 2013

From @bulk88

On Wed May 22 13​:57​:37 2013, kartlee05 wrote​:

Hi Folks,

We use perl widely for one of our job monitoring application. And
recently
we received reports from customer that the application crash after few
hours with 'invalid handle' exception in ntdll.dll.
So we planned to run our application in Application Verifier of
Windows to
trace handles and having a hard time to run perl under it. Currently
the
following piece of code extracted in the form of a sample script crash
under AppVerifier -
....................................................
Exactly after notepad.exe process is spawned we see the crash with
following stack trace. We see a similar trace at customer site when a
handle is being closed through closesocket(..) call which is not
really a
socket handle. The stack trace given below is from perl-5.10.1. We
also see
a similar stack trace with 5.14.2. So I am sure this is a problem even
in
current running version of perl. Can you please take a look and
respond
back?

NT Kernel does not throw exceptions AKA crashes AKA SEH for
STATUS_INVALID_HANDLE unless you turn on exotic debugging (GlobalFlag
key in registry for that image path) for the process, see
http​://msdn.microsoft.com/en-us/library/windows/hardware/ff542881%28v=vs.85%29.aspx
. That is what VS Debugger's invalid handle exceptions are. They are
made only for debugging purposes and dont happen at normal runtime.
STATUS_INVALID_HANDLE will be the returned error number for Native API
call in kernel32. Your customer would never have a crash with
STATUS_INVALID_HANDLE unless GlobalFlag is set in the registry by
accident, or some non-MS and non-Perl code did a RaiseException.

What my first guess is that this is a double free-ing of a socket
handle. My second guess says this is by design, because in
http​://perl5.git.perl.org/perl.git/blob/HEAD​:/win32/win32sck.c#l418 all
unix FDs get closed as a winsock handle, then if winsock doesn't
recognize it, then close it as a clib FD. The design I think is an
artifact that under Dos Windows (especially 95, late in 98 SE and ME,
winsock handles became kernel pipes I think), winsock handles were not
kernel handles (WSAGetLastError vs GetLastError, on NT WSA version
forwards to kernel32). On NT, all winsock handles are kernel pipe
handles belonging to the AFD driver. Note, Win32's/Perl's pipes are
implemented by a different driver called NPFS. So that is why this hack
is used to separate sockets from pipes,
http​://jakash3.wordpress.com/2012/12/19/getting-file-descriptor-type-in-windows/
. Calling the generic CloseHandle on a socket is forbidden by MS's docs,
so that is probably out of the question. I think the current code is
fine. Someone can argue speed of finding out the handle type and closing
it once vs blindly closing it with different libraries.

5.14 I think is now out of support since 5.18 was released a few days
ago. I didn't run any code to write this post.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented May 23, 2013

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Jun 6, 2013

From karthik.rajagopalan@schrodinger.com

Hi,

Thanks for your reply. I don't think debugging flag is turned on at
customer site. Otherwise you will see this when analyzing the dump (
NTGLOBALFLAG​: 0
APPLICATION_VERIFIER_FLAGS​: 0 ). BTW, the job monitor code we use is
purely perl, so there is no confusion that non-perl code is causing the
crash here or throwing exception. Please find below the new crash report we
received. This seem to be coming win32's alarm implementation. Any thoughts
about this crash?

-Karthik

0​:000> !analyze -v
*******************************************************************************
*
  *
* Exception Analysis
  *
*
  *
*******************************************************************************

*** ERROR​: Symbol file could not be found. Defaulted to export symbols for
threads.dll -
GetPageUrlData failed, server returned HTTP status 404
URL requested​:
http​://watson.microsoft.com/StageOne/perl_exe/0_0_0_0/51a63046/ntdll_dll/6_1_7601_17725/4ec4aa8e/c0000008/000cd7d8.htm?Retriage=1

FAULTING_IP​:
ntdll!RtlRaiseStatus+18
00000000`76e0d7d8 488b8424b8010000 mov rax,qword ptr [rsp+1B8h]

EXCEPTION_RECORD​: ffffffffffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress​: 0000000076e0d7d8 (ntdll!RtlRaiseStatus+0x0000000000000018)
  ExceptionCode​: c0000008 (Invalid handle)
  ExceptionFlags​: 00000001
NumberParameters​: 0
Thread tried to close a handle that was invalid or illegal to close

DEFAULT_BUCKET_ID​: INVALID_POINTER_READ

PROCESS_NAME​: perl.exe

ERROR_CODE​: (NTSTATUS) 0xc0000008 - An invalid HANDLE was specified.

EXCEPTION_CODE​: (NTSTATUS) 0xc0000008 - An invalid HANDLE was specified.

MOD_LIST​: <ANALYSIS/>

NTGLOBALFLAG​: 0

APPLICATION_VERIFIER_FLAGS​: 0

FAULTING_THREAD​: 0000000000001554

PRIMARY_PROBLEM_CLASS​: INVALID_POINTER_READ

BUGCHECK_STR​: APPLICATION_FAULT_INVALID_POINTER_READ

LAST_CONTROL_TRANSFER​: from 0000000076da8e59 to 0000000076e0d7d8

STACK_TEXT​:
00000000`0031de20 00000000`76da8e59 : 00000000`00000100 00000000`0031e670
00000000`00000000 00000000`0031e640 : ntdll!RtlRaiseStatus+0x18
00000000`0031e3c0 00000000`76d67e74 : 00000000`0031e600 00000000`00000000
00000000`c0150008 00000000`0031e600 : ntdll! ?? :​:FNODOBFM​::`string'+0x969e
00000000`0031e3f0 00000000`76d67b2e : 00000000`00000000 000007fe`fb9cbaf0
00000000`0031e690 000007fe`fd37d90d : ntdll!LdrpLoadDll+0x897
00000000`0031e600 000007fe`fd379aa9 : 00000000`00000000 00000000`00000000
000007fe`fb9cbaf0 00000000`00000062 : ntdll!LdrLoadDll+0x9a
00000000`0031e670 000007fe`fd37bc01 : 00000000`00000000 000007fe`fb9cbaf0
000007fe`fb9c5a10 00000000`00000000 : KERNELBASE!LoadLibraryExW+0x22e
00000000`0031e6e0 000007fe`fb98dffa : 00000000`00000000 00000065`00670061
00000000`00000000 00000000`00000000 : KERNELBASE!LoadLibraryExA+0x51
00000000`0031e730 000007fe`fb98dfb3 : 00000000`00000000 00000000`00000083
00000000`0049052a 00000000`00000001 : uxtheme!_delayLoadHelper2+0x96
00000000`0031e7c0 000007fe`fb98b192 : 00000000`0049052a 00000000`00010001
00000000`0031e860 00000000`00000004 : uxtheme!_tailMerge_dwmapi_dll+0x3f
00000000`0031e830 000007fe`fb9885a0 : 00000000`00000001 00000000`00000000
00000000`0049052a 00000000`00000083 : uxtheme!CThemeWnd​::Reject+0x58
00000000`0031e860 000007fe`fb981607 : 00000000`00000000 00000000`00000083
00000000`00000000 00000000`0049052a : uxtheme!CThemeWnd​::Attach+0x1cd
00000000`0031e8d0 000007fe`fb98b1c6 : 00000000`0031ec40 00000000`0049052a
00000000`00000000 00000000`0031ec40 : uxtheme!_ThemeDefWindowProc+0x133
00000000`0031e980 00000000`76c4aafc : 00000000`00000000 00000000`00000104
00000000`0031e9e8 00000000`0031e9f8 : uxtheme!ThemeDefWindowProcA+0xe
00000000`0031e9c0 00000000`6473b226 : 00000000`00000083 00000000`00000000
00000000`00000000 ffffffff`ffffffff : user32!DefWindowProcA+0xe6
00000000`0031ea10 00000000`76c59bd1 : 00000000`00000000 00000000`00000000
00000000`00000001 00000000`00000000 :
perl514!win32_message_window_proc+0x46
[c​:\perl\x64\perl-5.14.2\win32\win32.c @​ 4450]
00000000`0031ea40 00000000`76c572cb : 00000000`00000000 00000000`6473b1e0
00000000`00000000 00000000`00000000 : user32!UserCallWinProcCheckWow+0x1ad
00000000`0031eb00 00000000`76c506e8 : 00000000`76c5041a ffffffff`ffff0000
00000000`6473b1e0 00000000`0031ec58 : user32!DispatchClientMessage+0xc3
00000000`0031eb60 00000000`76d91225 : 00000000`00000000 fffff880`08baf790
00000000`00000000 00000000`00000000 : user32!_fnINOUTNCCALCSIZE+0x3c
00000000`0031ebc0 00000000`76c5041a : 00000000`76c50397 00000000`0031f088
00000000`00000000 00000000`0031f088 : ntdll!KiUserCallbackDispatcherContinue
00000000`0031ec58 00000000`76c50397 : 00000000`0031f088 00000000`00000000
00000000`0031f088 00000000`0031f088 : user32!ZwUserCreateWindowEx+0xa
00000000`0031ec60 00000000`76c505d8 : 00000000`0000002e 00000000`80000000
00000000`00000000 00000000`00000000 : user32!VerNtUserCreateWindowEx+0x27c
00000000`0031efd0 00000000`76c4a350 : 00000000`00000000 00000000`64870558
00000000`003da340 00000000`0000003c : user32!CreateWindowEx+0x404
00000000`0031f120 00000000`6473b657 : 00000000`0000000e 00000000`00683368
00000000`0000000e 00000000`6482bfbe : user32!CreateWindowExA+0x70
00000000`0031f1a0 00000000`6473b6af : 00000000`00000000 00000000`02a0b1d0
00000000`005955a8 00000000`647dd9d9 :
perl514!win32_create_message_window+0xa7
[c​:\perl\x64\perl-5.14.2\win32\win32.c @​ 4548]
00000000`0031f260 00000000`647bfd35 : 00000000`02a0b1d0 00000000`0363f910
00000000`003d33c8 00000000`00000001 : perl514!win32_alarm+0x3f
[c​:\perl\x64\perl-5.14.2\win32\win32.c @​ 2342]
00000000`0031f290 00000000`647942a6 : 00000000`005955a8 00000000`00000000
00000000`00000002 00000000`005955a8 : perl514!Perl_pp_alarm+0x55
[c​:\perl\x64\perl-5.14.2\pp_sys.c @​ 4565]
00000000`0031f2c0 00000000`64807991 : 00000000`00000001 00000000`6474cef0
00000000`003dc140 00000000`005955a8 : perl514!Perl_runops_standard+0x16
[c​:\perl\x64\perl-5.14.2\run.c @​ 41]
00000000`0031f2f0 00000000`64807c24 : 00000000`005955a8 00000000`00000000
00000000`0031f2d0 00000000`003da340 : perl514!S_run_body+0x131
[c​:\perl\x64\perl-5.14.2\win32\perl.c @​ 2352]
00000000`0031f320 00000000`6474d291 : 00000000`003d4620 00000000`003dc140
00000000`003da340 00000000`00000000 : perl514!perl_run+0x264
[c​:\perl\x64\perl-5.14.2\win32\perl.c @​ 2271]
00000000`0031f490 00000001`3f5811b2 : 00000000`00000001 00000000`00000000
00000000`00000000 00000000`00000000 : perl514!RunPerl+0x151
[c​:\perl\x64\perl-5.14.2\win32\perllib.c @​ 270]
00000000`0031f8d0 00000000`7667652d : 00000000`00000000 00000000`00000000
00000000`00000000 00000000`00000000 : perl!__tmainCRTStartup+0x11a
[f​:\dd\vctools\crt_bld\self_64_amd64\crt\src\crtexe.c @​ 555]
00000000`0031f900 00000000`76d6c521 : 00000000`00000000 00000000`00000000
00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
00000000`0031f930 00000000`00000000 : 00000000`00000000 00000000`00000000
00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d

STACK_COMMAND​: ~0s; .ecxr ; kb

FOLLOWUP_IP​:
uxtheme!_delayLoadHelper2+96
000007fe`fb98dffa 488bd8 mov rbx,rax

SYMBOL_STACK_INDEX​: 6

SYMBOL_NAME​: uxtheme!_delayLoadHelper2+96

FOLLOWUP_NAME​: MachineOwner

MODULE_NAME​: uxtheme

IMAGE_NAME​: uxtheme.dll

DEBUG_FLR_IMAGE_TIMESTAMP​: 4a5be093

FAILURE_BUCKET_ID​:
INVALID_POINTER_READ_c0000008_uxtheme.dll!_delayLoadHelper2

BUCKET_ID​:
X64_APPLICATION_FAULT_INVALID_POINTER_READ_uxtheme!_delayLoadHelper2+96

WATSON_STAGEONE_URL​:
http​://watson.microsoft.com/StageOne/perl_exe/0_0_0_0/51a63046/ntdll_dll/6_1_7601_17725/4ec4aa8e/c0000008/000cd7d8.htm?Retriage=1

Followup​: MachineOwner


On Wed, May 22, 2013 at 11​:04 PM, bulk88 via RT
<perlbug-followup@​perl.org>wrote​:

On Wed May 22 13​:57​:37 2013, kartlee05 wrote​:

Hi Folks,

We use perl widely for one of our job monitoring application. And
recently
we received reports from customer that the application crash after few
hours with 'invalid handle' exception in ntdll.dll.
So we planned to run our application in Application Verifier of
Windows to
trace handles and having a hard time to run perl under it. Currently
the
following piece of code extracted in the form of a sample script crash
under AppVerifier -
....................................................
Exactly after notepad.exe process is spawned we see the crash with
following stack trace. We see a similar trace at customer site when a
handle is being closed through closesocket(..) call which is not
really a
socket handle. The stack trace given below is from perl-5.10.1. We
also see
a similar stack trace with 5.14.2. So I am sure this is a problem even
in
current running version of perl. Can you please take a look and
respond
back?

NT Kernel does not throw exceptions AKA crashes AKA SEH for
STATUS_INVALID_HANDLE unless you turn on exotic debugging (GlobalFlag
key in registry for that image path) for the process, see

http​://msdn.microsoft.com/en-us/library/windows/hardware/ff542881%28v=vs.85%29.aspx
. That is what VS Debugger's invalid handle exceptions are. They are
made only for debugging purposes and dont happen at normal runtime.
STATUS_INVALID_HANDLE will be the returned error number for Native API
call in kernel32. Your customer would never have a crash with
STATUS_INVALID_HANDLE unless GlobalFlag is set in the registry by
accident, or some non-MS and non-Perl code did a RaiseException.

What my first guess is that this is a double free-ing of a socket
handle. My second guess says this is by design, because in
http​://perl5.git.perl.org/perl.git/blob/HEAD​:/win32/win32sck.c#l418 all
unix FDs get closed as a winsock handle, then if winsock doesn't
recognize it, then close it as a clib FD. The design I think is an
artifact that under Dos Windows (especially 95, late in 98 SE and ME,
winsock handles became kernel pipes I think), winsock handles were not
kernel handles (WSAGetLastError vs GetLastError, on NT WSA version
forwards to kernel32). On NT, all winsock handles are kernel pipe
handles belonging to the AFD driver. Note, Win32's/Perl's pipes are
implemented by a different driver called NPFS. So that is why this hack
is used to separate sockets from pipes,

http​://jakash3.wordpress.com/2012/12/19/getting-file-descriptor-type-in-windows/
. Calling the generic CloseHandle on a socket is forbidden by MS's docs,
so that is probably out of the question. I think the current code is
fine. Someone can argue speed of finding out the handle type and closing
it once vs blindly closing it with different libraries.

5.14 I think is now out of support since 5.18 was released a few days
ago. I didn't run any code to write this post.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Jun 16, 2013

From @bulk88

On Thu Jun 06 11​:27​:32 2013, kartlee05 wrote​:

Please find below the new crash
report we
received. This seem to be coming win32's alarm implementation. Any
thoughts
about this crash?

-Karthik

STACK_TEXT​:
00000000`0031de20 00000000`76da8e59 : 00000000`00000100
00000000`0031e670
00000000`00000000 00000000`0031e640 : ntdll!RtlRaiseStatus+0x18
00000000`0031e3c0 00000000`76d67e74 : 00000000`0031e600
00000000`00000000
00000000`c0150008 00000000`0031e600 : ntdll! ??
:​:FNODOBFM​::`string'+0x969e
00000000`0031e3f0 00000000`76d67b2e : 00000000`00000000
000007fe`fb9cbaf0
00000000`0031e690 000007fe`fd37d90d : ntdll!LdrpLoadDll+0x897
00000000`0031e600 000007fe`fd379aa9 : 00000000`00000000
00000000`00000000
000007fe`fb9cbaf0 00000000`00000062 : ntdll!LdrLoadDll+0x9a
00000000`0031e670 000007fe`fd37bc01 : 00000000`00000000
000007fe`fb9cbaf0
000007fe`fb9c5a10 00000000`00000000 : KERNELBASE!LoadLibraryExW+0x22e
00000000`0031e6e0 000007fe`fb98dffa : 00000000`00000000
00000065`00670061
00000000`00000000 00000000`00000000 : KERNELBASE!LoadLibraryExA+0x51
00000000`0031e730 000007fe`fb98dfb3 : 00000000`00000000
00000000`00000083
00000000`0049052a 00000000`00000001 : uxtheme!_delayLoadHelper2+0x96
00000000`0031e7c0 000007fe`fb98b192 : 00000000`0049052a
00000000`00010001
00000000`0031e860 00000000`00000004 :
uxtheme!_tailMerge_dwmapi_dll+0x3f
00000000`0031e830 000007fe`fb9885a0 : 00000000`00000001
00000000`00000000
00000000`0049052a 00000000`00000083 : uxtheme!CThemeWnd​::Reject+0x58
00000000`0031e860 000007fe`fb981607 : 00000000`00000000
00000000`00000083
00000000`00000000 00000000`0049052a : uxtheme!CThemeWnd​::Attach+0x1cd
00000000`0031e8d0 000007fe`fb98b1c6 : 00000000`0031ec40
00000000`0049052a
00000000`00000000 00000000`0031ec40 :
uxtheme!_ThemeDefWindowProc+0x133
00000000`0031e980 00000000`76c4aafc : 00000000`00000000
00000000`00000104
00000000`0031e9e8 00000000`0031e9f8 : uxtheme!ThemeDefWindowProcA+0xe
00000000`0031e9c0 00000000`6473b226 : 00000000`00000083
00000000`00000000
00000000`00000000 ffffffff`ffffffff : user32!DefWindowProcA+0xe6
00000000`0031ea10 00000000`76c59bd1 : 00000000`00000000
00000000`00000000
00000000`00000001 00000000`00000000 :
perl514!win32_message_window_proc+0x46
[c​:\perl\x64\perl-5.14.2\win32\win32.c @​ 4450]
00000000`0031ea40 00000000`76c572cb : 00000000`00000000
00000000`6473b1e0
00000000`00000000 00000000`00000000 :
user32!UserCallWinProcCheckWow+0x1ad
00000000`0031eb00 00000000`76c506e8 : 00000000`76c5041a
ffffffff`ffff0000
00000000`6473b1e0 00000000`0031ec58 :
user32!DispatchClientMessage+0xc3
00000000`0031eb60 00000000`76d91225 : 00000000`00000000
fffff880`08baf790
00000000`00000000 00000000`00000000 : user32!_fnINOUTNCCALCSIZE+0x3c
00000000`0031ebc0 00000000`76c5041a : 00000000`76c50397
00000000`0031f088
00000000`00000000 00000000`0031f088 :
ntdll!KiUserCallbackDispatcherContinue
00000000`0031ec58 00000000`76c50397 : 00000000`0031f088
00000000`00000000
00000000`0031f088 00000000`0031f088 : user32!ZwUserCreateWindowEx+0xa
00000000`0031ec60 00000000`76c505d8 : 00000000`0000002e
00000000`80000000
00000000`00000000 00000000`00000000 :
user32!VerNtUserCreateWindowEx+0x27c
00000000`0031efd0 00000000`76c4a350 : 00000000`00000000
00000000`64870558
00000000`003da340 00000000`0000003c : user32!CreateWindowEx+0x404
00000000`0031f120 00000000`6473b657 : 00000000`0000000e
00000000`00683368
00000000`0000000e 00000000`6482bfbe : user32!CreateWindowExA+0x70
00000000`0031f1a0 00000000`6473b6af : 00000000`00000000
00000000`02a0b1d0
00000000`005955a8 00000000`647dd9d9 :
perl514!win32_create_message_window+0xa7
[c​:\perl\x64\perl-5.14.2\win32\win32.c @​ 4548]
00000000`0031f260 00000000`647bfd35 : 00000000`02a0b1d0
00000000`0363f910
00000000`003d33c8 00000000`00000001 : perl514!win32_alarm+0x3f
[c​:\perl\x64\perl-5.14.2\win32\win32.c @​ 2342]
00000000`0031f290 00000000`647942a6 : 00000000`005955a8
00000000`00000000
00000000`00000002 00000000`005955a8 : perl514!Perl_pp_alarm+0x55
[c​:\perl\x64\perl-5.14.2\pp_sys.c @​ 4565]
00000000`0031f2c0 00000000`64807991 : 00000000`00000001
00000000`6474cef0
00000000`003dc140 00000000`005955a8 :
perl514!Perl_runops_standard+0x16
[c​:\perl\x64\perl-5.14.2\run.c @​ 41]
00000000`0031f2f0 00000000`64807c24 : 00000000`005955a8
00000000`00000000
00000000`0031f2d0 00000000`003da340 : perl514!S_run_body+0x131
[c​:\perl\x64\perl-5.14.2\win32\perl.c @​ 2352]
00000000`0031f320 00000000`6474d291 : 00000000`003d4620
00000000`003dc140
00000000`003da340 00000000`00000000 : perl514!perl_run+0x264
[c​:\perl\x64\perl-5.14.2\win32\perl.c @​ 2271]
00000000`0031f490 00000001`3f5811b2 : 00000000`00000001
00000000`00000000
00000000`00000000 00000000`00000000 : perl514!RunPerl+0x151
[c​:\perl\x64\perl-5.14.2\win32\perllib.c @​ 270]
00000000`0031f8d0 00000000`7667652d : 00000000`00000000
00000000`00000000
00000000`00000000 00000000`00000000 : perl!__tmainCRTStartup+0x11a
[f​:\dd\vctools\crt_bld\self_64_amd64\crt\src\crtexe.c @​ 555]
00000000`0031f900 00000000`76d6c521 : 00000000`00000000
00000000`00000000
00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
00000000`0031f930 00000000`00000000 : 00000000`00000000
00000000`00000000
00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d

Use Dependency Walker since something went wrong with loading a DLL. I'm
not familiar with NT 6 DLL loader, I know more about the NT 5 DLL
loader. I dont fully trust that call stack because of
"​::FNODOBFM​::`string'" which seems to be garbage according to google.
Also normally the callstacks have parameters (but they are garbage on
x64 because of calling convention), but here all I see is offsets from
functions. IIRC offsets from functions means the debugger is guessing
the function call names by at the export table and calculating an offset
from the closest exported function. Sometimes this is correct, sometimes
its very wrong. _delayLoadHelper2 means a delay loaded dll, in this case
dwmapi.dll. So, we are looking for why loading a DLL caused
STATUS_INVALID_HANDLE, as an exception, instead of it winding up.
Appverifier, a C debugger attached to the process, or a checked build
are what I would try to rule out. The links below describe security
software meddling with DLL loading.

Googling STATUS_INVALID_HANDLE and loadlibraryex gave me
http​://forums.asp.net/t/1704958.aspx/7/10?Re+SEHException+thrown+when+I+run+the+application
http​://social.msdn.microsoft.com/Forums/en-US/vsdebug/thread/06714a48-2125-4ee7-b0bd-1df3fa329c77/

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Oct 3, 2013

From @bulk88

I think I found another report
http​://www.tek-tips.com/viewthread.cfm?qid=1231893 of the bug in this
ticket, the bug being the mswsock!SockImportHandle calling
vfbasics!AVrfpNtDeviceIoControlFile with a bad OS handle.

On Server 2003, (not the NT 6 the OP is using), I tried a
"closesocket(2);", 2 being a garbage handle. If
WahReferenceContextByHandle in DSOCKET​::GetCountedDSocketFromSocket
returns 0, a tailcall is done to DSOCKET​::FindIFSSocket from
DSOCKET​::GetCountedDSocketFromSocket. WahReferenceContextByHandle turns
OS socket handles into user mode memory blocks/opaque pointers from the
Winsock service provider. DSOCKET​::FindIFSSocket starts doing syscalls
on the bogus handle. The NtQueryObject correctly returns
0xC0000008/STATUS_INVALID_HANDLE for bogus handle "2". I've never used
AppVerifier, but since Winsock will ALWAYS do syscalls on bogus handles
that never were socket handles in the history of the process,
AppVerifier will probably complain. The particular syscall/kernel call
from winsock that generates the invalid handle exception, is very likely
to change with each Windows OS, but it shows that there are detectable
side effects from doing a closesocket on a bogus or disk OS handle.

ntdll.dll!_NtQueryObject@​20() + 0x7 bytes
  kernel32.dll!_GetHandleInformation@​8() + 0x5f bytes
  ws2_32.dll!DSOCKET​::FindIFSSocket() + 0x1c bytes
  ws2_32.dll!_closesocket@​4() + 0x2f bytes
  API.dll!@​Call_asm@​16() Line 100 Asm
  API.dll!XS_Win32__API_ImportCall(interpreter * my_perl=0x01d49840, cv
* cv=0x00000004) Line 559 C
  perl519.dll!Perl_pp_entersub(interpreter * my_perl=0x00000000) Line
2764 C
  perl519.dll!Perl_runops_standard(interpreter * my_perl=0x01b64e74)
Line 42 + 0x4 bytes C
  perl519.dll!S_run_body(interpreter * my_perl=0x00000000, long
oldscope=1) Line 2500 + 0xa bytes C
  perl519.dll!perl_run(interpreter * my_perl=0x01b64e74) Line 2416 +
0x8 bytes C
  perl519.dll!RunPerl(int argc=5, char * * argv=0x01b64d68, char * *
env=0x01b63800) Line 270 + 0x6 bytes C++
  perl.exe!main(int argc=5, char * * argv=0x01b64d68, char * *
env=0x01b63800) Line 23 + 0x12 bytes C
  perl.exe!__tmainCRTStartup() Line 582 + 0x17 bytes C
  kernel32.dll!_BaseProcessStart@​4() + 0x28 bytes

@p5pRT
Copy link
Author

p5pRT commented Oct 30, 2013

From @steve-m-hay

For the record, I can reproduce this, debugging blead perl in VC++ 2010 with AppVerifier running. My call stack is​:

  ntdll.dll!77b1f921()
  [Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
  ntdll.dll!77b1f921()
  vfbasics.dll!50a936fa()
  mswsock.dll!73d76e77()
  vfbasics.dll!50a966d3()
  vfbasics.dll!50a966d3()

perl519.dll!Perl_get_context() Line 32 C
  perl519.dll!PerlIO_debug(const char * fmt, ...) Line 441 + 0x5 bytes C
  perl519.dll!win32_close(int fd) Line 3298 + 0x9 bytes C
  perl519.dll!PerlLIOClose(IPerlLIO * piPerl, int handle) Line 947 + 0x9 bytes C++
  perl519.dll!PerlIOUnix_close(interpreter * my_perl, _PerlIO * * f) Line 2859 + 0x1c bytes C
  perl519.dll!PerlIOBase_close(interpreter * my_perl, _PerlIO * * f) Line 2200 + 0x10 bytes C
  perl519.dll!PerlIOBuf_close(interpreter * my_perl, _PerlIO * * f) Line 4230 + 0xd bytes C
  perl519.dll!PerlIO__close(interpreter * my_perl, _PerlIO * * f) Line 1457 + 0x10 bytes C
  perl519.dll!Perl_PerlIO_close(interpreter * my_perl, _PerlIO * * f) Line 1470 + 0xd bytes C
  perl519.dll!Perl_do_openn(interpreter * my_perl, gv * gv, const char * oname, long len, int as_raw, int rawmode, int rawperm, _PerlIO * * supplied_fp, sv * * svp, long num_svs) Line 674 + 0xd bytes C
  perl519.dll!Perl_pp_open(interpreter * my_perl) Line 640 + 0x2e bytes C
  perl519.dll!Perl_runops_debug(interpreter * my_perl) Line 2274 + 0xf bytes C
  perl519.dll!S_run_body(interpreter * my_perl, long oldscope) Line 2520 + 0xf bytes C
  perl519.dll!perl_run(interpreter * my_perl) Line 2439 C
  perl519.dll!RunPerl(int argc, char * * argv, char * * env) Line 270 + 0x9 bytes C++
  perl.exe!main(int argc, char * * argv, char * * env) Line 23 + 0x12 bytes C
  perl.exe!__tmainCRTStartup() Line 555 + 0x17 bytes C
  kernel32.dll!7696336a()
  ntdll.dll!77b39f72()
  ntdll.dll!77b39f45()

@p5pRT

This comment has been minimized.

@p5pRT
Copy link
Author

p5pRT commented Oct 30, 2013

From @steve-m-hay

On Wed Oct 30 01​:49​:04 2013, shay wrote​:

For the record, I can reproduce this, debugging blead perl in VC++
2010 with AppVerifier running. My call stack is​:

I can also still reproduce this, with the same call stack, with the patch from #13328 (which is related but doesn't claim to fix this) applied.

@p5pRT
Copy link
Author

p5pRT commented Nov 9, 2013

From @bulk88

I'll add this here for archival reasons/google pickup.

People using Procmon (MS/Sysinternals Process Monitor) on a Perl process, may sometimes see a couple calls in sequence to NtDeviceIOControlFile with IOCTL 0x12043. The exact line in procmon is "INVALID PARAMETER Control​: 0x12043 (Device​:0x1 Function​:2064 Method​: 3)" with a valid on disk file name the Path column (in most cases a .pm module). Do not be alarmed. 0x12043 is IOCTL_AFD_GET_CONTEXT. The C callstack that generates the failed 0x12043 is


ntdll.dll!_ZwDeviceIoControlFile@​40()
  mswsock.dll!_SockImportHandle@​12() + 0xd2 bytes
  mswsock.dll!_SockFindAndReferenceSocket@​8() + 0x430a bytes
  mswsock.dll!_WSPGetSockOpt@​24() + 0x53 bytes
  ws2_32.dll!DCATALOG​::FindIFSProviderForSocket() + 0xa4 bytes
  ws2_32.dll!DSOCKET​::FindIFSSocket() + 0x37 bytes
  ws2_32.dll!_closesocket@​4() + 0x2f bytes
  perl519.dll!my_close(int fd=0) Line 695 C
  perl519.dll!win32_close(int fd=4) Line 3301 + 0x9 bytes C
  perl519.dll!PerlLIOClose(IPerlLIO * piPerl=0x01c4817c, int handle=4) Line 947 + 0x9 bytes C++
  perl519.dll!PerlIOUnix_close(interpreter * my_perl=0x01c45c54, _PerlIO * * f=0x00000000) Line 2859 + 0xb bytes C
  perl519.dll!PerlIOBase_close(interpreter * my_perl=0x01c45c54, _PerlIO * * f=0x01c66f24) Line 2200 + 0x8 bytes C
  perl519.dll!PerlIOBuf_close(interpreter * my_perl=0x01c45c54, _PerlIO * * f=0x01c66f24) Line 4231 C
  perl519.dll!Perl_PerlIO_close(interpreter * my_perl=0x01c45c54, _PerlIO * * f=0x01c66f24) Line 1470 + 0x24 bytes C
  perl519.dll!Perl_lex_next_chunk(interpreter * my_perl=0x01c45c54, unsigned long flags=2147483648) Line 1357 + 0x7 bytes C
  perl519.dll!Perl_yylex(interpreter * my_perl=0x01c45c54) Line 5344 + 0x7 bytes C
  perl519.dll!Perl_yyparse(interpreter * my_perl=0x01c45c54, int gramtype=258) Line 342 + 0x6 bytes C
  perl519.dll!S_doeval(interpreter * my_perl=0x00000000, int gimme=2, cv * outside=0x00000000, unsigned long seq=996, hv * hh=0x00000000) Line 3514 + 0x25 bytes C
  perl519.dll!Perl_pp_require(interpreter * my_perl=0x01c45c54) Line 4159 + 0x22 bytes C
  perl519.dll!Perl_runops_standard(interpreter * my_perl=0x01c45c54) Line 42 + 0x4 bytes C
  perl519.dll!S_run_body(interpreter * my_perl=0x00000000, long oldscope=1) Line 2433 + 0xa bytes C
  perl519.dll!perl_run(interpreter * my_perl=0x01c45c54) Line 2349 + 0x8 bytes C
  perl519.dll!RunPerl(int argc=3, char * * argv=0x01c45be8, char * * env=0x01c440e0) Line 270 + 0x6 bytes C++
  perl.exe!__tmainCRTStartup() Line 582 + 0x17 bytes C
  kernel32.dll!_BaseProcessStart@​4() + 0x28 bytes


Below is a sample from procmon on my machine of what the error looks like. I got alarmed seeing it, since IOCTLs on disk files are very very rare in procmon logs, so curiosity and a wonder if I have anti-security or pro-security software running on the machine made me investigate it.


9​:25​:30.3284509 PM perl.exe 21088 CreateFile C​:\p519\src\lib\Errno.pm SUCCESS Desired Access​: Generic Read, Disposition​: Open, Options​: Synchronous IO Non-Alert, Non-Directory File, Attributes​: N, ShareMode​: Read, Write, AllocationSize​: n/a, OpenResult​: Opened
9​:25​:30.3299015 PM perl.exe 21088 QueryInformationVolume C​:\p519\src\lib\Errno.pm SUCCESS VolumeCreationTime​: 5/26/2011 2​:06​:18 PM, VolumeSerialNumber​: rmved, SupportsObjects​: True, VolumeLabel​:
9​:25​:30.3313223 PM perl.exe 21088 QueryAllInformationFile C​:\p519\src\lib\Errno.pm BUFFER OVERFLOW CreationTime​: 11/7/2013 4​:49​:58 PM, LastAccessTime​: 11/7/2013 4​:49​:56 PM, LastWriteTime​: 11/7/2013 4​:49​:58 PM, ChangeTime​: 11/7/2013 4​:49​:58 PM, FileAttributes​: RA, AllocationSize​: 118,784, EndOfFile​: 117,001, NumberOfLinks​: 1, DeletePending​: False, Directory​: False, IndexNumber​: 0x800000009b826, EaSize​: 0, Access​: Generic Read, Position​: 0, Mode​: Synchronous IO Non-Alert, AlignmentRequirement​: Long
9​:25​:30.3332354 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 0, Length​: 8,192
9​:25​:30.3359461 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 8,192, Length​: 8,192
9​:25​:30.3380665 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 16,384, Length​: 8,192
9​:25​:30.3400322 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 24,576, Length​: 8,192
9​:25​:30.3420694 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 32,768, Length​: 8,192
9​:25​:30.3440664 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 40,960, Length​: 8,192
9​:25​:30.3459751 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 49,152, Length​: 8,192
9​:25​:30.3479488 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 57,344, Length​: 8,192
9​:25​:30.3498458 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 65,536, Length​: 8,192
9​:25​:30.3519996 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 73,728, Length​: 8,192
9​:25​:30.3540562 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 81,920, Length​: 8,192
9​:25​:30.3563836 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 90,112, Length​: 8,192
9​:25​:30.3587679 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 98,304, Length​: 8,192
9​:25​:30.3610166 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 106,496, Length​: 8,192
9​:25​:30.3722478 PM perl.exe 21088 ReadFile C​:\p519\src\lib\Errno.pm SUCCESS Offset​: 114,688, Length​: 2,313
9​:25​:30.3754717 PM perl.exe 21088 DeviceIoControl C​:\p519\src\lib\Errno.pm INVALID PARAMETER Control​: 0x12043 (Device​:0x1 Function​:2064 Method​: 3)
9​:25​:30.3783409 PM perl.exe 21088 DeviceIoControl C​:\p519\src\lib\Errno.pm INVALID PARAMETER Control​: 0x12043 (Device​:0x1 Function​:2064 Method​: 3)
9​:25​:30.3811440 PM perl.exe 21088 DeviceIoControl C​:\p519\src\lib\Errno.pm INVALID PARAMETER Control​: 0x12043 (Device​:0x1 Function​:2064 Method​: 3)
9​:25​:30.3840694 PM perl.exe 21088 DeviceIoControl C​:\p519\src\lib\Errno.pm INVALID PARAMETER Control​: 0x12043 (Device​:0x1 Function​:2064 Method​: 3)
9​:25​:30.3868833 PM perl.exe 21088 DeviceIoControl C​:\p519\src\lib\Errno.pm INVALID PARAMETER Control​: 0x12043 (Device​:0x1 Function​:2064 Method​: 3)
9​:25​:30.3882475 PM perl.exe 21088 CloseFile C​:\p519\src\lib\Errno.pm SUCCESS


The "(Device​:0x1 Function​:2064 Method​: 3)" is total garbage BTW. Device 1 (FILE_DEVICE_BEEP) is the system beeper. I dont hear beeps coming out of the machine. Something else is up.


#define FILE_DEVICE_NETWORK 18
.....
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
#define _AFD_CONTROL_CODE(Operation,Method) \
  ((FSCTL_AFD_BASE)<<12 | (Operation<<2) | Method)
......
#define AFD_GET_CONTEXT 16
......
#define METHOD_NEITHER 3
......
#define IOCTL_AFD_GET_CONTEXT \
  _AFD_CONTROL_CODE(AFD_GET_CONTEXT, METHOD_NEITHER)


((18<<12)=0x12000, so ioctl call that starts with this is winsock related


C​:\Documents and Settings\Administrator>perl -e"printf('%x',((0x12<<12)|(0x10<<2
)|3)");
12043
C​:\Documents and Settings\Administrator>


But the definition of IOCTL bitfield that MS and Promon use is different from winsock's _AFD_CONTROL_CODE

#define CTL_CODE(DeviceType, Function, Method, Access) \
  (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))

Once the ioctl code 0x12043 is decoded as a CTL_CODE it is "(Device​:0x1 Function​:2064 Method​: 3)".

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Nov 23, 2013

From @bulk88

On Wed Oct 30 02​:08​:09 2013, shay wrote​:

On Wed Oct 30 01​:49​:04 2013, shay wrote​:

For the record, I can reproduce this, debugging blead perl in VC++
2010 with AppVerifier running. My call stack is​:

I can also still reproduce this, with the same call stack, with the
patch from #120091 (which is related but doesn't claim to fix this)
applied.

Posting a patch to this problem for comments/review. The idea is to tag sockets handles with a special low 2 bits pattern. The kernel ignores the last 2 bits of all handles, so the only meaning the low 2 bits have are on a user-mode (that includes kernel32.dll) level. 2 closesocket calls are necessary to avoid a leak in ws2_32.dll's socket descriptor to winsock provider vtable hash table. I'm not 100% sure that this won't create another race like #118059 fixed. This patch breaks non-IFS/non-kernel handle socket provider protocols even harder than before. Before=Perl already calls dup/dup2 on socket handles, which calls DuplicateHandle in the CRT, if the socket handle isn't a kernel handle (but a user mode pointer), that causes breakage. Also I assume Perl's sysread/syswrite/buffered IO calls also won't work on non-kernel socket handles, I think I've seen a ticket about that somewhere on RT before. Perl may have had support for doing recv instead of read() on sockets in the past, but I might be imagining it, will need to research.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Nov 23, 2013

From @bulk88

0001-WIP-don-t-closesocket-on-non-socket-handles.patch
From b431f923ee773478da393bb26dd45b07d1dbf7e8 Mon Sep 17 00:00:00 2001
From: Daniel Dragan <bulk88@hotmail.com>
Date: Sat, 23 Nov 2013 17:03:32 -0500
Subject: [PATCH] WIP don't closesocket on non-socket handles

---
 win32/win32.c    |   19 ++++++++++-
 win32/win32.h    |   29 ++++++++++++++++++
 win32/win32io.c  |    5 ++-
 win32/win32sck.c |   86 ++++++++++++++++++++++++++++++++++-------------------
 4 files changed, 105 insertions(+), 34 deletions(-)

diff --git a/win32/win32.c b/win32/win32.c
index fb24bbb..8a565d8 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -3332,13 +3332,28 @@ win32_isatty(int fd)
 DllExport int
 win32_dup(int fd)
 {
-    return dup(fd);
+    int ret;
+    int old = _osfhnd(fd);
+    //DebugBreak();
+    ret = dup(fd);
+    if(ret != -1) {
+        int h = _osfhnd(ret);
+        assert(!SOCKET_FLAG_FROM_HANDLE(h));
+        _osfhnd(ret) |= SOCKET_FLAG_FROM_HANDLE(_osfhnd(fd));
+    }
+    return ret;
 }
 
 DllExport int
 win32_dup2(int fd1,int fd2)
 {
-    return dup2(fd1,fd2);
+    int ret = dup2(fd1,fd2);
+    if(ret != -1) {
+        int h = _osfhnd(fd2);
+        assert(!SOCKET_FLAG_FROM_HANDLE(h));
+        _osfhnd(fd2) |= SOCKET_FLAG_FROM_HANDLE(_osfhnd(fd1));
+    }
+    return ret;
 }
 
 DllExport int
diff --git a/win32/win32.h b/win32/win32.h
index a521a41..df51ffa 100644
--- a/win32/win32.h
+++ b/win32/win32.h
@@ -599,6 +599,35 @@ EXTERN_C _CRTIMP ioinfo* __pioinfo[];
 
 /* since we are not doing a dup2(), this works fine */
 #  define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = (intptr_t)osfh)
+
+/* derived from IsConsoleHandle macro, (((ULONG_PTR)(h) & 0x10000003) == 0x3)
+   if low 2 bits are 11, then its a console psuedo handle that only Win32
+   subsystem understands, not a kernel handle, a kernel handle ends in 00, we
+   reserve 10 to mean socket handle, starting in Windows 8 according to reports
+   online, console handles are kernel handles, low 2 bits behaviour on Win8
+   consone handles is unresearched, 0x80000000 catches negative values are
+   other psuedo handles or invalid handle, meaning behind flag 0x10000000 is
+   unknown, 01 can not be used since 01 handles are listed as FILE_TYPE_UNKNOWN,
+   and the handle is not passed to a kernel call to determine its type, this
+   causes _fstati64 to fail, making open() from PP fail when opening a numeric
+   fd that contains 01 tagged handle
+*/
+
+//static void
+//PerlIOUnix_setfd(pTHX_ PerlIO *f, int fd, int imode)
+//{
+//    PerlIOUnix * const s = PerlIOSelf(f, PerlIOUnix);
+//#if defined(WIN32)
+//    Stat_t st;
+//    if (PerlLIO_fstat(fd, &st) == 0) { <<<<< calls GetFileType in CRT which makes fstat fail
+//        
+#  define HANDLE_IS_SCK 0x2
+#  define PSUEDO_HANDLES_MASK 0x90000003
+#  define IS_PSUEDO_HANDLE(h) ((ULONG_PTR)(h) & PSUEDO_HANDLES_MASK)
+#  define SOCKET_FLAG_FROM_HANDLE(h) ((IS_PSUEDO_HANDLE(h) == HANDLE_IS_SCK) ? HANDLE_IS_SCK : 0x0)
+/* slow, and does failing kernel calls, use only in DEBUGGING */
+#  define PUBLIC_IS_SOCKET(h) (GetFileType(h) == FILE_TYPE_PIPE && GetNamedPipeInfo(h, 0, 0, 0, 0) == 0)
+
 #endif /* PERL_CORE */
 
 /* IO.xs and POSIX.xs define PERLIO_NOT_STDIO to 1 */
diff --git a/win32/win32io.c b/win32/win32io.c
index d183e3b..817628e 100644
--- a/win32/win32io.c
+++ b/win32/win32io.c
@@ -321,7 +321,10 @@ PerlIOWin32_dup(pTHX_ PerlIO *f, PerlIO *o, CLONE_PARAMS *params, int flags)
  if (DuplicateHandle(proc, os->h, proc, &new_h, 0, FALSE,  DUPLICATE_SAME_ACCESS))
   {
    char mode[8];
-   int fd = win32_open_osfhandle((intptr_t) new_h, PerlIOUnix_oflags(PerlIO_modestr(o,mode)));
+   int fd;
+   assert(!(SOCKET_FLAG_FROM_HANDLE(new_h) || IS_PSUEDO_HANDLE(new_h)));
+   *((ULONG_PTR *)&new_h) |= SOCKET_FLAG_FROM_HANDLE(os->h);
+   fd = win32_open_osfhandle((intptr_t) new_h, PerlIOUnix_oflags(PerlIO_modestr(o,mode)));
    if (fd >= 0)
     {
      f = PerlIOBase_dup(aTHX_ f, o, params, flags);
diff --git a/win32/win32sck.c b/win32/win32sck.c
index 674add2..df92946 100644
--- a/win32/win32sck.c
+++ b/win32/win32sck.c
@@ -396,7 +396,11 @@ win32_accept(SOCKET s, struct sockaddr *addr, int *addrlen)
     SOCKET r;
 
     SOCKET_TEST((r = accept(TO_SOCKET(s), addr, addrlen)), INVALID_SOCKET);
-    return OPEN_SOCKET(r);
+    assert(!(SOCKET_FLAG_FROM_HANDLE(r) || IS_PSUEDO_HANDLE(r)));
+    r = OPEN_SOCKET(r);
+    if( r != -1)
+        _osfhnd(r) |= HANDLE_IS_SCK;
+    return r;
 }
 
 int
@@ -670,8 +674,13 @@ win32_socket(int af, int type, int protocol)
 
     if((s = open_ifs_socket(af, type, protocol)) == INVALID_SOCKET)
 	errno = get_last_socket_error();
-    else
+    else {
+        assert(!(SOCKET_FLAG_FROM_HANDLE(s) || IS_PSUEDO_HANDLE(s)));
+        /* _open_osfhandle does a GetFileType which bans handles ending in 01 */
 	s = OPEN_SOCKET(s);
+        if( s != -1)
+            _osfhnd(s) |= HANDLE_IS_SCK;
+    }
 
     return s;
 }
@@ -690,21 +699,28 @@ int my_close(int fd)
 	return(close(fd));	/* Then not a socket. */
     osf = TO_SOCKET(fd);/* Get it now before it's gone! */
     if (osf != -1) {
-	int err;
-	err = closesocket(osf);
-	if (err == 0) {
-	    assert(_osfhnd(fd) == osf); /* catch a bad ioinfo struct def */
-	    /* don't close freed handle */
-	    _set_osfhnd(fd, INVALID_HANDLE_VALUE);
-	    return close(fd);
+	if(SOCKET_FLAG_FROM_HANDLE(osf)) {
+            int err = closesocket(osf);
+            int err2 = closesocket(osf&~HANDLE_IS_SCK);
+            assert(err == err2);  /* ???? */
+            if (err == 0) {
+                assert(_osfhnd(fd) == osf); /* catch a bad ioinfo struct def */
+                /* don't close freed handle */
+                _set_osfhnd(fd, INVALID_HANDLE_VALUE);
+                return close(fd);
+            }
+            else if (err == SOCKET_ERROR) {
+                err = get_last_socket_error();
+                assert(err != ENOTSOCK);
+                /* note this does a not allowed by MS CloseHandle
+                   on the socket handle */
+                (void)close(fd);
+                errno = err;
+                return EOF;
+            }
 	}
-	else if (err == SOCKET_ERROR) {
-	    err = get_last_socket_error();
-	    if (err != ENOTSOCK) {
-		(void)close(fd);
-		errno = err;
-		return EOF;
-	    }
+	else {
+	    assert(!PUBLIC_IS_SOCKET(osf));
 	}
     }
     return close(fd);
@@ -721,21 +737,29 @@ my_fclose (FILE *pf)
     if (osf != -1) {
 	int err;
 	win32_fflush(pf);
-	err = closesocket(osf);
-	if (err == 0) {
-	    assert(_osfhnd(win32_fileno(pf)) == osf); /* catch a bad ioinfo struct def */
-	    /* don't close freed handle */
-	    _set_osfhnd(win32_fileno(pf), INVALID_HANDLE_VALUE);
-	    return fclose(pf);
-	}
-	else if (err == SOCKET_ERROR) {
-	    err = get_last_socket_error();
-	    if (err != ENOTSOCK) {
-		(void)fclose(pf);
-		errno = err;
-		return EOF;
-	    }
-	}
+        if(SOCKET_FLAG_FROM_HANDLE(osf)) {
+            int err = closesocket(osf);
+            int err2 = closesocket(osf&~HANDLE_IS_SCK);
+            assert(err == err2); /* ???? */
+            if (err == 0) {
+                assert(_osfhnd(win32_fileno(pf)) == osf); /* catch a bad ioinfo struct def */
+                /* don't close freed handle */
+                _set_osfhnd(win32_fileno(pf), INVALID_HANDLE_VALUE);
+                return fclose(pf);
+            }
+            else if (err == SOCKET_ERROR) {
+                err = get_last_socket_error();
+                assert(err != ENOTSOCK);
+                /* note this does a not allowed by MS CloseHandle
+                   on the socket handle */
+                (void)fclose(pf);
+                errno = err;
+                return EOF;
+            }
+        }
+        else {
+            assert(!PUBLIC_IS_SOCKET(osf));
+        }
     }
     return fclose(pf);
 }
-- 
1.7.9.msysgit.0

@p5pRT
Copy link
Author

p5pRT commented Dec 23, 2013

From @bulk88

Bump.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Dec 23, 2013

From @bulk88

This ticket needs "Operating System" to be changed to "mswin32". I dont have the perms to do it.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Dec 23, 2013

From @khwilliamson

On 12/22/2013 08​:42 PM, bulk88 via RT wrote​:

This ticket needs "Operating System" to be changed to "mswin32". I dont have the perms to do it.

Done

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2014

From @bulk88

On Sun Dec 22 19​:40​:08 2013, bulk88 wrote​:

Bump.

Bump.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Jan 27, 2014

From @steve-m-hay

On Sat Nov 23 14​:16​:48 2013, bulk88 wrote​:

Posting a patch to this problem for comments/review. The idea is to
tag sockets handles with a special low 2 bits pattern. The kernel
ignores the last 2 bits of all handles, so the only meaning the low 2
bits have are on a user-mode (that includes kernel32.dll) level. 2
closesocket calls are necessary to avoid a leak in ws2_32.dll's socket
descriptor to winsock provider vtable hash table. I'm not 100% sure
that this won't create another race like #118059 fixed. This patch
breaks non-IFS/non-kernel handle socket provider protocols even harder
than before. Before=Perl already calls dup/dup2 on socket handles,
which calls DuplicateHandle in the CRT, if the socket handle isn't a
kernel handle (but a user mode pointer), that causes breakage. Also I
assume Perl's sysread/syswrite/buffered IO calls also won't work on
non-kernel socket handles, I think I've seen a ticket about that
somewhere on RT before. Perl may have had support for doing recv
instead of read() on sockets in the past, but I might be imagining it,
will need to research.

What is the breakage caused to non-IFS providers that you refer to? I'm wondering what effect it will have on previous changes in this area​:

036c1c1 - Fix [perl #24269] socket() call uses non-IFS providers causing subsequent print/read to hang or misbehave

1c97260 - Implement new environment variable to allow the use of non-IFS compatible LSP's on Windows to allow Perl to work in conjunction with a firewall such as McAfee Guardian

@p5pRT
Copy link
Author

p5pRT commented Mar 5, 2014

From @bulk88

On Mon Jan 27 10​:12​:36 2014, shay wrote​:

On Sat Nov 23 14​:16​:48 2013, bulk88 wrote​:

Posting a patch to this problem for comments/review. The idea is to
tag sockets handles with a special low 2 bits pattern. The kernel
ignores the last 2 bits of all handles, so the only meaning the low 2
bits have are on a user-mode (that includes kernel32.dll) level. 2
closesocket calls are necessary to avoid a leak in ws2_32.dll's
socket
descriptor to winsock provider vtable hash table. I'm not 100% sure
that this won't create another race like #118059 fixed. This patch
breaks non-IFS/non-kernel handle socket provider protocols even
harder
than before. Before=Perl already calls dup/dup2 on socket handles,
which calls DuplicateHandle in the CRT, if the socket handle isn't a
kernel handle (but a user mode pointer), that causes breakage. Also I
assume Perl's sysread/syswrite/buffered IO calls also won't work on
non-kernel socket handles, I think I've seen a ticket about that
somewhere on RT before. Perl may have had support for doing recv
instead of read() on sockets in the past, but I might be imagining
it,
will need to research.

What is the breakage caused to non-IFS providers that you refer to?
I'm wondering what effect it will have on previous changes in this
area​:

036c1c1 - Fix [perl #24269] socket() call uses non-IFS providers
causing subsequent print/read to hang or misbehave

That seems to have prohibited non-IFS protocols if I read it correctly, since if the protocol doesn't have "XP1_IFS_HANDLES" a socket using that protocol can't be created. The OP in 24269 is unclear, but he refers to sockets and kernel IO operations like WriteFile, and kernel I/O handle scheduling. He says "SO_SYNCHRONOUS_NONALERT" but i think he meant WSA_FLAG_OVERLAPPED.

1c97260 - Implement new environment variable to allow the use of
non-IFS compatible LSP's on Windows to allow Perl to work in
conjunction with a firewall such as McAfee Guardian

That creates an option to removal the prohibition of non-IFS protocols.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Jul 9, 2015

From @bulk88

On Wed Mar 05 05​:05​:34 2014, bulk88 wrote​:

On Mon Jan 27 10​:12​:36 2014, shay wrote​:

Bump. Steve Hay? Tony C? JDB?

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Jul 9, 2015

From @tonycoz

On Sat Nov 23 14​:16​:48 2013, bulk88 wrote​:

The kernel
ignores the last 2 bits of all handles,

Do Microsoft document that anywhere?

Tony

@p5pRT
Copy link
Author

p5pRT commented Jul 9, 2015

From @bulk88

On Wed Jul 08 22​:45​:47 2015, tonyc wrote​:

On Sat Nov 23 14​:16​:48 2013, bulk88 wrote​:

The kernel
ignores the last 2 bits of all handles,

Do Microsoft document that anywhere?

Tony

http​://blogs.msdn.com/b/oldnewthing/archive/2008/08/27/8898863.aspx
http​://blogs.msdn.com/b/oldnewthing/archive/2005/01/21/358109.aspx

From MS headers


#define STDAPI EXTERN_C HRESULT STDAPICALLTYPE
#define STDAPI_(type) EXTERN_C type STDAPICALLTYPE

#define STDMETHODIMP HRESULT STDMETHODCALLTYPE
#define STDMETHODIMP_(type) type STDMETHODCALLTYPE

// The 'V' versions allow Variable Argument lists.

#define STDAPIV EXTERN_C HRESULT STDAPIVCALLTYPE
#define STDAPIV_(type) EXTERN_C type STDAPIVCALLTYPE

#define STDMETHODIMPV HRESULT STDMETHODVCALLTYPE
#define STDMETHODIMPV_(type) type STDMETHODVCALLTYPE

// end_winnt

//
// Low order two bits of a handle are ignored by the system and available
// for use by application code as tag bits. The remaining bits are opaque
// and used to store a serial number and table index.
//

#define OBJ_HANDLE_TAGBITS 0x00000003L

//
// Cardinal Data Types [0 - 2**N-2)
//

typedef char CCHAR; // winnt
typedef short CSHORT;
typedef ULONG CLONG;

typedef CCHAR *PCCHAR;
typedef CSHORT *PCSHORT;
typedef CLONG *PCLONG;

// end_ntminiport end_ntndis end_ntminitape


Reactos code

http​://doxygen.reactos.org/d0/d54/ntdef_8h_source.html#l01577

use of low 2 bits to store CWD directory handle inside Kernel32/ntdll http​://doxygen.reactos.org/d9/d6e/lib_2rtl_2path_8c_source.html#l00026

reactos defintion of a kernel handle


00054 typedef union _EXHANDLE
00055 {
00056 struct
00057 {
00058 ULONG_PTR TagBits​:2;
00059 ULONG_PTR Index​:29;
00060 };
00061 struct
00062 {
00063 ULONG_PTR TagBits2​:2;
00064 ULONG_PTR LowIndex​:HANDLE_LOW_BITS;
00065 ULONG_PTR MidIndex​:HANDLE_HIGH_BITS;
00066 ULONG_PTR HighIndex​:HANDLE_HIGH_BITS;
00067 ULONG_PTR KernelFlag​:KERNEL_FLAG_BITS;
00068 };
00069 HANDLE GenericHandleOverlay;
00070 ULONG_PTR Value;
00071 } EXHANDLE, *PEXHANDLE;
00072


http​://doxygen.reactos.org/de/d51/ntoskrnl_2ex_2handle_8c_source.html#l00045 at line 45 the low 2 bits are cleared when converting a handle to a kernel pointer


MS's version
-----------cut-------------------
PHANDLE_TABLE_ENTRY
ExpLookupHandleTableEntry (
  IN PHANDLE_TABLE HandleTable,
  IN EXHANDLE tHandle
  )
{
-----------cut-------------------
  EXHANDLE Handle;
-----------cut-------------------
  ULONG_PTR MaxHandle;
-----------cut-------------------
  //
  // Extract the handle index
  //
  Handle = tHandle;

  Handle.TagBits = 0;

  MaxHandle = *(volatile ULONG *) &HandleTable->NextHandleNeedingPool;


It is obvious I will have to rebase and clean up the commit a bit more before it can be applied.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Jul 13, 2015

From @tonycoz

On Thu Jul 09 04​:10​:20 2015, bulk88 wrote​:

On Wed Jul 08 22​:45​:47 2015, tonyc wrote​:

On Sat Nov 23 14​:16​:48 2013, bulk88 wrote​:

The kernel
ignores the last 2 bits of all handles,

Do Microsoft document that anywhere?

http​://blogs.msdn.com/b/oldnewthing/archive/2008/08/27/8898863.aspx
http​://blogs.msdn.com/b/oldnewthing/archive/2005/01/21/358109.aspx

As we discussed in IRC, my main concern was whether the sockets returned
by socket() were also kernel handles and had those reserved bits.

From our discussion, I think we can reasonably assume that for, whether some
badly written anti-virus or firewall we can only find out in the real world.

As also discussed, duplicating a socket with your patch produces a handle
that we have flagged as a socket, but since the CRT dup() function just
does a DuplicateHandle() winsock doesn't think it's a socket and fails to
close it.

If some external C code decides to dup() the fd without the mapping in XSUB.h
visible, this will also produce a bad fd, but there's not really anything
we can do about it.

Tony

@p5pRT
Copy link
Author

p5pRT commented Jul 21, 2015

From @bulk88

On Sun Jul 12 19​:14​:04 2015, tonyc wrote​:

As we discussed in IRC, my main concern was whether the sockets
returned
by socket() were also kernel handles and had those reserved bits.

Perl would act very bizarre and flawed if diamond operator failed on sockets that got fdreopened/duped. Also, I dont think that win32_accept or win32_socket would even return a "fd" for a user mode socket. The SOCKET */maybe handle is turned into a fd with OPEN_SOCKET/win32_open_osfhandle/_open_osfhandle. _open_osfhandle calls GetFileType, if GetFileType fails/returns FILE_TYPE_UNKNOWN (same val), MS CRT wont alloc a fd. GetFileType is mostly a wrapper around NtQueryVolumeInformationFile, which obviously requires a real kernel handle.


int __cdecl _open_osfhandle(
  intptr_t osfhandle,
  int flags
  )
{
  int fh;
  char fileflags; /* _osfile flags */
  DWORD isdev; /* device indicator in low byte */

  /* copy relevant flags from second parameter */

...........cut.....................

  /* find out what type of file (file/device/pipe) */

  isdev = GetFileType((HANDLE)osfhandle);
  if (isdev == FILE_TYPE_UNKNOWN) {
  /* OS error */
  _dosmaperr( GetLastError() ); /* map error */
  return -1;
  }


Perl internally has IoTYPE_SOCKET flag but I am not sure if reopening a fd preserves IoTYPE_SOCKET flag or not, and if there is adequate testing of the flag (and how to see the flag from PP without B​::/Devel​::Peek). http​://perl5.git.perl.org/perl.git/commitdiff/036c1c1eb70a0dfc5a7187959eb5e39d499c9396 and http​://perl5.git.perl.org/perl.git/commitdiff/1c97260979b979af03b946d71d50e8e4c075665c would basically have to go away, possibly replaced with panics in win32_accept and win32_socket (the only 2 ways to make a socket, correct me if I am wrong) if the low 2 bits are not 0. There is also the problem, that assuming a SOCKET * is a memory addr instead of kernel handle, the low 2 bits will be zero because that private C struct behind the SOCKET * is going to be aligned to atleast 4 bytes if not 8 or 16, so low 2 bits being zero can't separate a mem addr socket from a handle socket. So maybe either the open_ifs_socket/WSCEnumProtocols/XP1_IFS_HANDLES code stays, or GetHandleInformation has to be used to figure out if its a kernel handle socket.

There is a slightly remote remote (should I round it to impossible?) chance that a mem addr might be a valid handle if over ~350K handles have been allocated. Windows can not allocate VM below addr 0x00010000 and on my system most handles are between 0x100 and 0x1000, and 0x2000 at tops. I'll skip the details (nobody wants a tour of Win32 VM layout), so the first possible malloc address is 0x150000. 0x150000/4=0x54000=344064 kernel handles. http​://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx says the limit is 16 million handles. So a [perl] process with mem addr SOCKETs, plus a large handle leak, could wind up mem addr socket pointers that are valid kernel handles, although the failure in this case would be that the panic croak of GetHandleInformation stops being thrown and GetHandleInformation returns true, is a kernel handle, even though the number is really a user mode SOCKET * and not a handle, and by coincidence, user mode SOCKET * happens to be a handle (maybe a file, maybe a mutex, maybe a process). But talking about deciding if its a user mode SOCKET or a kernel handle SOCKET and supporting user mode sockets is all in vain if _open_osfhandle will never allocate a fd for a user mode socket.

User mode sockets aren't inheritable http​://stackoverflow.com/questions/11847793/are-tcp-socket-handles-inheritable

From our discussion, I think we can reasonably assume that for,
whether some
badly written anti-virus or firewall we can only find out in the real
world.

Would anyone still use such a firewall in the 2010s? How many non-perl apps (php/python/ruby/non interpretated lang progs) would be broken if sockets aren't kernel handles?

Now lets see what other engines do about closesocket vs close/CloseHandle on Win32.

http​://caml.inria.fr/mantis/view.php?id=5258 ocaml remembers what is a file and what is a socket

CPYTHON

cpython doesn't use _open_osfhandle on sockets, sockets are not fds https://docs.python.org/2/library/socket.html

"
Note that there are no methods read() or write(); use recv() and send() without flags argument instead.


socket.fileno()

  Return the socket’s file descriptor (a small integer). This is useful with select.select().

  Under Windows the small integer returned by this method cannot be used where a file descriptor can be used (such as os.fdopen()). Unix does not have this limitation.
"
https://github.com/python/cpython/blob/master/Modules/socketmodule.c the "fd/fileno" is the SOCKET */kernel handle, no _open_osfhandle

https://github.com/python/cpython/blob/master/Modules/posixmodule.c#L8484 only use of _open_osfhandle that isn't on stderr/stdout/stdin in cpython

https://github.com/python/cpython/blob/master/Lib/multiprocessing/connection.py#L354 socket object knows its a socket and not a fd on win32

PHP

https://github.com/php/php-src/blob/master/ext/sockets/windows_common.h#L34
https://github.com/php/php-src/blob/master/ext/sockets/sockets.c#L492
http​://php.net/manual/en/book.sockets.php

sockets are socket objects/class, they know they aren't fds on Win32, no fileno method, no _open_osfhandle

"fastcgi" knows if its a socket or not
https://github.com/php/php-src/blob/PHP-5.6.11/sapi/cgi/fastcgi.c#L756
https://github.com/php/php-src/blob/PHP-5.6.11/sapi/cgi/fastcgi.c#L1112

RUBY

https://github.com/ruby/ruby/blob/trunk/win32/win32.c#L7374
https://github.com/ruby/ruby/blob/trunk/win32/win32.c#L6025
https://github.com/ruby/ruby/blob/trunk/win32/win32.c#L2425
https://github.com/ruby/ruby/blob/trunk/win32/win32.c#L713

Ruby in a separate table remembers what handles are sockets. That is an analog of using Perl's ptr_table_fetch
https://github.com/ruby/ruby/blob/trunk/st.c#L383

Also Ruby does not use _open_osfhandle the way Perl does
https​://github.com/ruby/ruby/blob/trunk/win32/win32.c#L2353 instead ruby creates a dummy handle, then swaps handles in ioinfo struct that backs the CRT's fd. I wonder why, ruby/ruby@92e4b1b not very explaining. In designing the handle tagging patch, 01 tagged handles fail GetFileType that _open_osfhandle does internally, which causes _open_osfhandle to fail. By calling CRT _open_osfhandle with a dummy valid handle, then swaping handles in the CRT struct, you could stick user mode sockets in there that fail GetFileType (not sure if that is useful).

As also discussed, duplicating a socket with your patch produces a
handle
that we have flagged as a socket, but since the CRT dup() function
just
does a DuplicateHandle() winsock doesn't think it's a socket and fails
to
close it.

The attached patch tries fix that by comparing the error codes from closesocket on the original and tagged handles.

  if (errtagged == erruntagged) /* this catches WSAENOTSOCK */
  err = erruntagged;
  else if (errtagged != WSAENOTSOCK && erruntagged == WSAENOTSOCK)
  err = errtagged;
  else if (errtagged == WSAENOTSOCK && erruntagged != WSAENOTSOCK)
  err = erruntagged;
  else
  DebugBreak(); /* 2 different states, which to pass to caller ??? */

If both closesockets return errors that are not WSAENOTSOCK, which error code is passed up to the caller from perl's my_close? IDK what it should be. Always the untagged version? which is the older/original handle.

There is also a little bit of reservations I have, since this patch increases the memory that each socket takes up by one extra DSOCKET object in ws2_32.dll (0x18 bytes long), and 0xd8 bytes inside mswsock.dll at this callstack

  ntdll.dll!_RtlAllocateHeap@​12() + 0xf
  mswsock.dll!_SockImportHandle@​8() + 0x199
  mswsock.dll!_SockFindAndReferenceSocket@​8() + 0x90a9
  mswsock.dll!_WSPGetSockOpt@​24() + 0x54
  ws2_32.dll!DCATALOG​::FindIFSProviderForSocket() + 0x95
  ws2_32.dll!DSOCKET​::FindIFSSocket() + 0x37
  ws2_32.dll!_closesocket@​4() + 0x2f
  perl523.dll!my_close(int fd=4) Line 719 + 0x9 C
  perl523.dll!PerlLIOClose(IPerlLIO * piPerl=0x00365b8c, int handle=4) Line 944 + 0x9 C
  perl523.dll!PerlIOUnix_close(interpreter * my_perl=0x00363efc, _PerlIO * * f=0x00000000) Line 2804 + 0xb C
  perl523.dll!PerlIOBase_close(interpreter * my_perl=0x00363efc, _PerlIO * * f=0x008f568c) Line 2120 + 0x8 C
  perl523.dll!PerlIOBuf_close(interpreter * my_perl=0x00363efc, _PerlIO * * f=0x008f568c) Line 4229 C
  perl523.dll!PerlIO__close(interpreter * my_perl=0x00363efc, _PerlIO * * f=0xfffffffe) Line 1382 + 0x7 C
  perl523.dll!Perl_PerlIO_close(interpreter * my_perl=0x00363efc, _PerlIO * * f=0x008f568c) Line 1395 + 0x10 C
  perl523.dll!Perl_io_close(interpreter * my_perl=0x00000000, io * io=0x71a871e0, gv * gv=0x00000000, char not_implicit=0, char warn_on_fail=0) Line 1091 + 0x9 C
  perl523.dll!Perl_sv_clear(interpreter * my_perl=0x00363efc, sv * const orig_sv=0x0093c92c) Line 6443 + 0x25 C
  perl523.dll!Perl_sv_free2(interpreter * my_perl=0x00363efc, sv * const sv=0x0093c92c, const unsigned long rc=1) Line 6885 C
  perl523.dll!S_SvREFCNT_dec_NN(interpreter * my_perl=0x00363efc, sv * sv=0xfffffffe) Line 177 + 0xb C
  perl523.dll!do_clean_named_io_objs(interpreter * my_perl=0x00363efc, sv * const sv=0x00921f6c) Line 625 + 0x6 C
  perl523.dll!S_visit(interpreter * my_perl=0x00363efc, void (interpreter *, sv *)* f=0x2809033b, const unsigned long flags=32777, const unsigned long mask=49407) Line 502 C
  perl523.dll!Perl_sv_clean_objs(interpreter * my_perl=0x00000000) Line 660 C
  perl523.dll!perl_destruct(interpreter * my_perl=0x00363efc) Line 804 C
  perl523.dll!RunPerl(int argc=3, char * * argv=0x01363de8, char * * env=0x00362f38) Line 263 C
  perl.exe!mainCRTStartup() Line 398 + 0xe C
  kernel32.dll!_BaseProcessStart@​4() + 0x23

There is also the CPU overhead of the "importing", but currently when perl is closesocket'ing disk files and pipes once winsock has been loaded/started to be used in the process, the "importing" is happening but failing.

The ideal solution would be to get IoTYPE_SOCKET down to unix layer, and have unix layer pass it onto my_close_maybe_a_socket.

  perl523.dll!PerlIOUnix_open(interpreter * my_perl=0x00363efc, _PerlIO_funcs * self=0x280f86c0, PerlIO_list_s * layers=0x008f547c, long n=0, const char * mode=0x2814fbd4, int fd=3, int imode=0, int perm=0, _PerlIO * * f=0x00000000, int narg=0, sv * * args=0x00000000) Line 2668 C
  perl523.dll!PerlIOBuf_open(interpreter * my_perl=0x00363efc, _PerlIO_funcs * self=0x280f8880, PerlIO_list_s * layers=0x008f547c, long n=1, const char * mode=0x2814fbd4, int fd=3, int imode=0, int perm=0, _PerlIO * * f=0x00000000, int narg=0, sv * * args=0x00000000) Line 3858 + 0x1b C
  perl523.dll!PerlIO_openn(interpreter * my_perl=0x00363efc, const char * layers=0x00000000, const char * mode=0x2814fbd4, int fd=3, int imode=0, int perm=0, _PerlIO * * f=0x00000000, int narg=0, sv * * args=0x00000000) Line 1561 + 0x1d C
  perl523.dll!PerlIO_fdopen(int fd=3, const char * mode=0x2814fbd4) Line 4886 + 0x16 C
  perl523.dll!Perl_pp_socket(interpreter * my_perl=0x00000006) Line 2484 C
  perl523.dll!Perl_runops_standard(interpreter * my_perl=0x00363efc) Line 41 + 0x4 C
  perl523.dll!S_run_body(interpreter * my_perl=0x00000000, long oldscope=1) Line 2448 + 0xa C
  perl523.dll!perl_run(interpreter * my_perl=0x00363efc) Line 2371 + 0x8 C
  perl523.dll!RunPerl(int argc=3, char * * argv=0x01363de8, char * * env=0x00362f38) Line 258 + 0x6 C
  perl.exe!mainCRTStartup() Line 398 + 0xe C
  kernel32.dll!_BaseProcessStart@​4() + 0x23

mode arg shows

+ mode 0x2814fbd4 "rb" const char *

for me, not "s" or "rs"

"b" is

#define PIPESOCK_MODE "b" /* pipes, sockets default to binmode */

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Jul 21, 2015

From @bulk88

0001-WIP-118127-don-t-closesocket-on-non-socket-handles-o.patch
From 04461bd1387d842e9c6d431e1ed9e9927631c198 Mon Sep 17 00:00:00 2001
From: Daniel Dragan <bulk88@hotmail.com>
Date: Tue, 21 Jul 2015 02:48:44 -0400
Subject: [PATCH] WIP #118127 don't closesocket on non-socket handles on Win32

-win32io.c changes due to
win32io.c(336) : error C2296: '|=' : illegal, left operand has type 'HANDLE'
---
 inline.h         |   10 ++++
 win32/win32.c    |   32 +++++++++++++-
 win32/win32.h    |   44 +++++++++++++++++++
 win32/win32io.c  |    7 +++-
 win32/win32sck.c |  122 ++++++++++++++++++++++++++++++++++++++++--------------
 5 files changed, 181 insertions(+), 34 deletions(-)

diff --git a/inline.h b/inline.h
index 46a8cb6..3e99e96 100644
--- a/inline.h
+++ b/inline.h
@@ -350,6 +350,16 @@ S_should_warn_nl(const char *pv) {
 
 #endif
 
+/* ------------------ win32sck.c ------------ */
+
+#if defined(WIN32) && !defined(WIN32_NO_SOCKETS)
+PERL_STATIC_INLINE BOOL
+S_is_handle_valid(HANDLE hnd) {
+    DWORD dwFlags;
+    return GetHandleInformation(hnd, &dwFlags);
+}
+#endif
+
 /* ------------------ pp.c, regcomp.c, toke.c, universal.c ------------ */
 
 #define MAX_CHARSET_NAME_LENGTH 2
diff --git a/win32/win32.c b/win32/win32.c
index 1ec045e..211db9f 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -3328,13 +3328,41 @@ win32_isatty(int fd)
 DllExport int
 win32_dup(int fd)
 {
-    return dup(fd);
+    int ret;
+    ret = dup(fd);
+#ifndef WIN32_NO_SOCKETS
+    if(ret != -1) {
+	int old_h = _osfhnd(fd);
+	int flag = SOCKET_FLAG_FROM_HANDLE(old_h);
+	int new_h = _osfhnd(ret);
+	assert(!SOCKET_FLAG_FROM_HANDLE(new_h)
+	       && PUBLIC_IS_SOCKET(new_h) == PUBLIC_IS_SOCKET(old_h));
+	new_h |= flag;
+	_osfhnd(ret) = new_h;
+    }
+#endif
+    return ret;
 }
 
 DllExport int
 win32_dup2(int fd1,int fd2)
 {
-    return dup2(fd1,fd2);
+    int ret;
+    ret = dup2(fd1,fd2);
+#ifndef WIN32_NO_SOCKETS
+    if(ret != -1) {
+	/* assert(!SOCKET_FLAG_FROM_HANDLE(_osfhnd(fd2)));
+	_osfhnd(fd2) |= SOCKET_FLAG_FROM_HANDLE(_osfhnd(fd1)); */
+	int old_h = _osfhnd(fd1);
+	int flag = SOCKET_FLAG_FROM_HANDLE(old_h);
+	int new_h = _osfhnd(fd2);
+	assert(!SOCKET_FLAG_FROM_HANDLE(new_h)
+	       && PUBLIC_IS_SOCKET(new_h) == PUBLIC_IS_SOCKET(old_h));
+	new_h |= flag;
+	_osfhnd(fd2) = new_h;
+    }
+#endif
+    return ret;
 }
 
 DllExport int
diff --git a/win32/win32.h b/win32/win32.h
index 3b35b6c..5d90839 100644
--- a/win32/win32.h
+++ b/win32/win32.h
@@ -625,6 +625,50 @@ EXTERN_C _CRTIMP ioinfo* __pioinfo[];
 
 /* since we are not doing a dup2(), this works fine */
 #  define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = (intptr_t)osfh)
+
+/* derived from IsConsoleHandle macro, (((ULONG_PTR)(h) & 0x10000003) == 0x3)
+   if low 2 bits are 11, then its a console psuedo handle that only Win32
+   subsystem understands, not a kernel handle, a kernel handle ends in 00, we
+   reserve 10 to mean socket handle, starting in Windows 8 according to reports
+   online, console handles are kernel handles, low 2 bits behaviour on Win8
+   consone handles is unresearched, 0x80000000 catches negative values are
+   other psuedo handles (STD_INPUT_HANDLE/-10/STD_OUTPUT_HANDLE/-11) or invalid
+   handle (-1), meaning behind flag 0x10000000 is unknown, 01 can not be used
+   since 01 handles are special cased to fail by GetFileType,
+   and the handle is not passed to a kernel call to determine its type, this
+   causes _fstati64 to fail, making open() from PP fail when opening a numeric
+   fd that contains 01 tagged handle, thus leaving 10 as the only permutation
+   that can be used.
+*/
+
+/*
+static void
+PerlIOUnix_setfd(pTHX_ PerlIO *f, int fd, int imode)
+{
+    PerlIOUnix * const s = PerlIOSelf(f, PerlIOUnix);
+#if defined(WIN32)
+    Stat_t st;
+    if (PerlLIO_fstat(fd, &st) == 0) { <<<<< calls GetFileType in CRT which makes fstat fail
+*/
+
+#  ifndef WIN32_NO_SOCKETS
+#    define HANDLE_IS_SCK 0x2
+#    define PSUEDO_HANDLES_MASK 0x90000003
+#    define IS_PSUEDO_HANDLE(h) ((ULONG_PTR)(h) & PSUEDO_HANDLES_MASK)
+#    define SOCKET_FLAG_FROM_HANDLE(h) ((IS_PSUEDO_HANDLE(h) == HANDLE_IS_SCK) ? HANDLE_IS_SCK : 0x0)
+/* [use] public [api to find out if the handle] is [a] socket
+
+   Slow, and does failing kernel calls, use only in DEBUGGING, not for regular builds.
+   There is no simple way to find out what is a socket, since the NT drivers for
+   named pipes (NPFS) and TCPIP (AFD or ws2ifsl) both call themselves
+   FILE_TYPE_PIPE/FILE_DEVICE_NAMED_PIPE. GetNamedPipeInfo internally is an
+   named pipe driver only ioctl. If the handle/driver sucessfully responds to a
+   NPFS ioctl, it proves that the handle is a pipe. Else it is assumed (but it
+   can't be proven without doing a winsock call) to be a socket.
+*/
+#    define PUBLIC_IS_SOCKET(h) (GetFileType((HANDLE)h) == FILE_TYPE_PIPE \
+                                && GetNamedPipeInfo((HANDLE)h, 0, 0, 0, 0) == 0)
+#  endif
 #endif /* PERL_CORE */
 
 /* IO.xs and POSIX.xs define PERLIO_NOT_STDIO to 1 */
diff --git a/win32/win32io.c b/win32/win32io.c
index 00f5bb8..7f06f28 100644
--- a/win32/win32io.c
+++ b/win32/win32io.c
@@ -329,7 +329,12 @@ PerlIOWin32_dup(pTHX_ PerlIO *f, PerlIO *o, CLONE_PARAMS *params, int flags)
  if (DuplicateHandle(proc, os->h, proc, &new_h, 0, FALSE,  DUPLICATE_SAME_ACCESS))
   {
    char mode[8];
-   int fd = win32_open_osfhandle((intptr_t) new_h, PerlIOUnix_oflags(PerlIO_modestr(o,mode)));
+   int fd;
+#ifndef WIN32_NO_SOCKETS
+   assert(!(SOCKET_FLAG_FROM_HANDLE(new_h) || IS_PSUEDO_HANDLE(new_h)));
+   *((ULONG_PTR *)&new_h) |= SOCKET_FLAG_FROM_HANDLE(os->h);
+#endif
+   fd = win32_open_osfhandle((intptr_t) new_h, PerlIOUnix_oflags(PerlIO_modestr(o,mode)));
    if (fd >= 0)
     {
      f = PerlIOBase_dup(aTHX_ f, o, params, flags);
diff --git a/win32/win32sck.c b/win32/win32sck.c
index 3f97241..07bda9f 100644
--- a/win32/win32sck.c
+++ b/win32/win32sck.c
@@ -396,7 +396,14 @@ win32_accept(SOCKET s, struct sockaddr *addr, int *addrlen)
     SOCKET r;
 
     SOCKET_TEST((r = accept(TO_SOCKET(s), addr, addrlen)), INVALID_SOCKET);
-    return OPEN_SOCKET(r);
+#ifndef WIN32_NO_SOCKETS
+    assert(!(SOCKET_FLAG_FROM_HANDLE(r) || IS_PSUEDO_HANDLE(r)));
+    if (r != INVALID_SOCKET)
+	r |= HANDLE_IS_SCK;
+#endif
+    /* why is this call creating a fd for an invalud socket ??? */
+    r = OPEN_SOCKET(r);
+    return r;
 }
 
 int
@@ -619,6 +626,15 @@ open_ifs_socket(int af, int type, int protocol)
     int error_code;
     SOCKET out = INVALID_SOCKET;
 
+/* looking all this up on every socket call is stupid,
+ everything (including the convert_proto_info_w2a calls, should use WSASocketW instead)
+ but the for loop needs to be factored out into StartSockets, assuming this
+ code from Winsock 1 era isn't completly removed
+
+ https://rt.perl.org/Ticket/Display.html?id=24269
+
+ http://perl5.git.perl.org/perl.git/commitdiff/036c1c1eb70a0dfc5a7187959eb5e39d499c9396
+ */
     if ((s = PerlEnv_getenv("PERL_ALLOW_NON_IFS_LSP")) && atoi(s))
         return WSASocket(af, type, protocol, NULL, 0, 0);
 
@@ -661,6 +677,8 @@ open_ifs_socket(int af, int type, int protocol)
     return out;
 }
 
+#ifndef WIN32_NO_SOCKETS
+
 SOCKET
 win32_socket(int af, int type, int protocol)
 {
@@ -670,8 +688,12 @@ win32_socket(int af, int type, int protocol)
 
     if((s = open_ifs_socket(af, type, protocol)) == INVALID_SOCKET)
 	errno = get_last_socket_error();
-    else
+    else {
+	assert(!(SOCKET_FLAG_FROM_HANDLE(s) || IS_PSUEDO_HANDLE(s)));
+	if (s != INVALID_SOCKET)
+	    s |= HANDLE_IS_SCK;
 	s = OPEN_SOCKET(s);
+    }
 
     return s;
 }
@@ -683,6 +705,7 @@ win32_socket(int af, int type, int protocol)
  *   -- BKS, 11-11-2000
 */
 
+
 int my_close(int fd)
 {
     int osf;
@@ -690,22 +713,49 @@ int my_close(int fd)
 	return(close(fd));	/* Then not a socket. */
     osf = TO_SOCKET(fd);/* Get it now before it's gone! */
     if (osf != -1) {
-	int err;
-	err = closesocket(osf);
-	if (err == 0) {
-	    assert(_osfhnd(fd) == osf); /* catch a bad ioinfo struct def */
-	    /* don't close freed handle */
-	    _set_osfhnd(fd, INVALID_HANDLE_VALUE);
-	    return close(fd);
-	}
-	else if (err == SOCKET_ERROR) {
-	    err = get_last_socket_error();
-	    if (err != ENOTSOCK) {
+	if(SOCKET_FLAG_FROM_HANDLE(osf)) {
+	    int err;
+	    int osftagged = osf;
+	    int errtagged = closesocket(osftagged) == SOCKET_ERROR ?  WSAGetLastError() : ERROR_SUCCESS;
+	    int osfuntagged = osftagged &~ HANDLE_IS_SCK;
+            int erruntagged = closesocket(osfuntagged)  == SOCKET_ERROR ?  WSAGetLastError() : ERROR_SUCCESS;
+/* use Socket; socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname(\"tcp\"));
+open(SOCKTWO, '<&' . fileno(SOCK)); close(SOCKTWO);
+this code sample uses dup/DuplicateHandle and creates a tagged handle,
+that is never once passed to a socket function, until the 1st closesocket, and then
+the 2nd closesocket fails with WSAENOTSOCK since winsock front end never got a func call on the untagged handle before, and attempting to vivify/"Import" the untagged handle fails, since the socket was closed in the WSP layer/mswsock.dll/afd.sys in the 1st closesocket, and on the 2nd closesocket, WSP layer/mswsock.dll/afd.sys does not tell the front end "yes I created this handle/socket"
+
+Therefore, only if tagged and untagged both return WSAENOTSOCK, will the result be WSAENOTSOCK, else the result of the operation will be something other than WSAENOTSOCK
+*/
+	    /* both kernel handles should be dead by now */
+	    assert(!S_is_handle_valid((HANDLE)osfuntagged)
+		   && !S_is_handle_valid((HANDLE)osftagged));
+	    if (errtagged == erruntagged) /* this catches WSAENOTSOCK */
+		err = erruntagged;
+	    else if (errtagged != WSAENOTSOCK && erruntagged == WSAENOTSOCK)
+		err = errtagged;
+	    else if (errtagged == WSAENOTSOCK && erruntagged != WSAENOTSOCK)
+		err = erruntagged;
+	    else
+		DebugBreak(); /* 2 different states, which to pass to caller ??? */
+	    if (err == ERROR_SUCCESS) {
+		assert(_osfhnd(fd) == osftagged); /* catch a bad ioinfo struct def */
+		/* don't close freed handle */
+		_set_osfhnd(fd, INVALID_HANDLE_VALUE);
+		return close(fd);
+	    }
+	    else {
+		assert(err != ENOTSOCK);
+		/* note close() does a not allowed by MS CloseHandle
+		   on the socket handle */
 		(void)close(fd);
-		errno = err;
+		errno = convert_wsa_error_to_errno(err);
 		return EOF;
 	    }
 	}
+	else {
+	    assert(!PUBLIC_IS_SOCKET(osf));
+	}
     }
     return close(fd);
 }
@@ -715,30 +765,40 @@ int
 my_fclose (FILE *pf)
 {
     int osf;
+    int fd;
     if (!wsock_started)		/* No WinSock? */
 	return(fclose(pf));	/* Then not a socket. */
-    osf = TO_SOCKET(win32_fileno(pf));/* Get it now before it's gone! */
+    osf = TO_SOCKET(fd = win32_fileno(pf));/* Get it now before it's gone! */
     if (osf != -1) {
-	int err;
 	win32_fflush(pf);
-	err = closesocket(osf);
-	if (err == 0) {
-	    assert(_osfhnd(win32_fileno(pf)) == osf); /* catch a bad ioinfo struct def */
-	    /* don't close freed handle */
-	    _set_osfhnd(win32_fileno(pf), INVALID_HANDLE_VALUE);
-	    return fclose(pf);
-	}
-	else if (err == SOCKET_ERROR) {
-	    err = get_last_socket_error();
-	    if (err != ENOTSOCK) {
-		(void)fclose(pf);
-		errno = err;
-		return EOF;
-	    }
-	}
+        if(SOCKET_FLAG_FROM_HANDLE(osf)) {
+            /* this code does not match my_close ATM, my_close is authoritative */
+            int err = closesocket(osf);
+            int err2 = closesocket(osf&~HANDLE_IS_SCK);
+            assert(err == err2); /* ???? */
+            if (err == 0) {
+                assert(_osfhnd(fd) == osf); /* catch a bad ioinfo struct def */
+                /* don't close freed handle */
+                _set_osfhnd(fd, INVALID_HANDLE_VALUE);
+                return fclose(pf);
+            }
+            else if (err == SOCKET_ERROR) {
+                err = get_last_socket_error();
+                assert(err != ENOTSOCK);
+                /* note this does a not allowed by MS CloseHandle
+                   on the socket handle */
+                (void)fclose(pf);
+                errno = err;
+                return EOF;
+            }
+        }
+        else {
+            assert(!PUBLIC_IS_SOCKET(osf));
+        }
     }
     return fclose(pf);
 }
+#endif /* #ifndef WIN32_NO_SOCKETS */
 
 struct hostent *
 win32_gethostbyaddr(const char *addr, int len, int type)
-- 
1.7.9.msysgit.0

@toddr
Copy link
Member

toddr commented Feb 12, 2020

@tonycoz @iabyn @bulk88 I see a patch as the last entry here. Are we still uncomfortable with the approach?

@xenu xenu removed the Severity Low label Dec 29, 2021
jddurand added a commit to jddurand/perlbuildwindows that referenced this issue Nov 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants