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

FW: Problem with Winsock based Exxx constants in VS2010+

From:
Steve Hay
Date:
September 16, 2013 15:01
Subject:
FW: Problem with Winsock based Exxx constants in VS2010+
Message ID:
C69C5F337BA04D49984AA7C86FFB6E777191@UKMAIL03.planit.group
Forwarding some private emails regarding Winsock based Exxx constants to p5p for archiving, as suggested by Jan.
The changes discussed are now committed in ea95436966.


-----Original Message-----
From: Jan Dubois [mailto:jand@activestate.com]
Sent: 14 September 2013 02:10
To: Steve Hay
Subject: Re: Problem with Winsock based Exxx constants in VS2010+

Hi Steve,

Please go ahead and commit as you see fit.  I don't seem to be getting any time to look into this in more details than the high-level feedback I gave you so far.

Also feel free to forward any part of our conversation to p5p if you feel that it should have a permanent record in the archives.

Cheers,
-Jan

----------------------------------------------------------------------
From:	Steve Hay
Sent:	08 September 2013 00:48
To:	Jan Dubois
Subject:	RE: Problem with Winsock based Exxx constants in VS2010+
Attachments:	test.c; test.pl; vc6test.c.txt; vc6test.pl.txt; vc10test.c.txt; vc10test.pl.txt

Jan Dubois wrote on 2013-08-31:
> On Fri, Aug 23, 2013 at 9:51 AM, Steve Hay 
> <Steve.Hay@verosoftware.com>
> wrote:
> [...]
> 
>> To avoid that, I imagine I would have to intercept any assignment to $!
>> and do the WSAExxx -> Exxx mapping there, rather than in a function 
>> only available to the core in win32sck.c as I have currently done. 
>> Does that sound feasible/sensible?
> 
> Yes, I think this is an excellent idea!

I've now implemented the interception of assignment to $! in Perl_magic_set(), 
which does indeed fix the Net::Ping test breakage. 


> 
>> Finally, I also found (after searching for more hard-coded 100xy
>> numbers) that IO::Socket contains a test for $! == 10022, which will 
>> be broken with the new scheme because $! will now be set to 22 rather 
>> than 10022. This is a nasty problem which I haven't yet got a plan 
>> for how to solve. The Errno/POSIX value of EINVAL is and has always 
>> been 22, so although testing against that will work nicely now, no 
>> existing code will be using that because it would have needed to test 
>> for 10022 (which there is no named constant for).
>> 
>> So to check for EINVAL one would have to write something like this:
>> 
>>         if ($! == (($^O eq 'MSWin32' && $] < 5.019004) ? 10022 :
>> EINVAL))
>> 
>> which doesn't seem very friendly.
> 
> It *might* be possible to solve this with overload magic, but I'm 
> afraid that this might require turning $! into a blessed reference (so 
> that the overload code can find the right methods for performing the 
> comparison).  It sounds a bit too convoluted to deal with just a few 
> backward compatibility cases.

I've left this out for now. I agree that the breakage concerned is 
sufficiently minor that it doesn't warrant such complications, but I will keep 
this possible solution in mind for the future -- there's nothing to stop it 
being done at a later date if it turns out to be more of a problem than 
anticipated.


> 
> [...]
> 
> One thing that I'm still wondering about is the stringification of 
> errno values. If you look at win32_strerror, you'll see that we have 
> special code to stringify errno values larger than sys_nerr via a call 
> to FormatMessage() using the system message catalog.
> 
> So I wonder if strerror() in msvcrt.dll will provide the correct 
> stringification for these additional error codes, especially on older 
> operating systems.  I guess this is not an issue if you compile
> *everything* with VC10, but I'm also thinking about mixing compilers 
> for Perl itself and for the modules.

Thank you -- I hadn't been aware of that code. I don't think that it's a 
(new!) problem, though, since it turns out that sys_nerr is still 43 in VC10, 
exactly as it was in VC6->VC9. So, in fact, that function is currently broken 
in VC10 builds anyway because errno.h codes in the range 100->140 get wrongly 
treated as winerror.h codes, e.g. 

perl -le "$!=106; print $!"

prints "Insert the diskette for drive %1." (the message for 
ERROR_SEM_USER_LIMIT) rather than "An established connection was aborted by 
the software in your host machine." (the message for ECONNABORTED, which is 
surely what would have been intended in the context in which the function is 
used).

I've now fixed that in my branch by mapping codes in the range 100->140 back 
to winerror.h/winsock2.h codes for VC10+ (or, in fact, any compiler which 
happens to have 100 in errno.h).


> 
> ActivePerl will be using a Mingw64 based compiler for 5.20 (same as 
> Strawberry), so it will continue to link against msvcrt.dll. I haven't 
> checked if the headers include the new E* constants, but if they do, 
> will they work properly for the version of MSVCRT.dll available on WinXP?

MinGW-w64/gcc-4.7.1 does not contain errnos higher than 42. I have no idea if 
they might change that in the future, but given that msvcr100.dll's strerror() 
doesn't provide messages for codes above 42 anyway (I tried it out in a 
standalone C program, btw: strerror(106) just returns "Unknown error") I don't 
think it will be an issue: the solution that I've implemented should work 
whether errno.h contains the extra errnos or not, regardless of which C run-
time is being used. 


[...]
> 
> PS: I found the corresponding mapping function in the Tcl source, in 
> case you want to take a look for comparison:
> 
> http://core.tcl.tk/tcl/artifact/055bdd9a7b71a8b90490e87da97623686d73b4 
> 1
> 4

I had a look at that, and also a (surprisingly-similar-to-perl's!) function in 
Ruby's win32/win32.c, and I don't think they cover anything relevant that I've 
missed. (They both handle more winerror.h error constants than just the 
winsock2.h ones, but we only seem to assign sockets errors to $! in perl, so 
I've ignored the rest.)

I have attached a script which shows what number ends up in $!, and how that 
sringifies, when all relevant values are assigned to $!, together with the 
outputs in a VC6 build and a VC10 build -- both on Windows 7, but I also tried 
on Windows XP and got identical results. I also tried a MinGW-w64 (gcc-4.7.1) 
build on both machines and got identical results to the VC6 builds.

The results are all as I would now expect, except for one minor fly in the 
ointment, namely that this:

perl -le "$!=10022; print $!"

now prints "Invalid argument" (the strerror() string for 22, which is actually 
what gets assigned to $!) rather than "An invalid argument was supplied." (the 
FormatMessage() string for 10022), but given that perl now assigns errno.h 
values rather than winsock2.h values to $! I don't think this really matters. 
(The handling of this $!=10022 case is only for backwards-compatibility with 
user code that may still be doing that.)

My branch also fixes the problems that I currently have with mod_perl. I've 
also attached a C program that outputs all relevant Exxx constants after 
including the Perl headers in the manner of XS extensions, plus its output in 
VC6 and VC10 builds (and again, MinGW-w64/gcc-4.7.1 is the same as VC6). The 
output is also as I would now expect, with ECONNABORTED being 106 rather than 
10053.

Many thanks for your comments on all this. All the changes can be seen in my 
updated smoke-me/steveh/errno branch (commit a52ed91023) if you have any time 
to look.

If all goes well with the smoking then I would like to commit this soon (in 
time for 5.19.4 on 20th would be nice, but is not essential), unless you would 
prefer that I hold off until you have a chance to take a closer look yourself?

Cheers,
Steve

----------------------------------------------------------------------
From:	Jan Dubois
Sent:	31 August 2013 02:19
To:	Steve Hay
Subject:	Re: Problem with Winsock based Exxx constants in VS2010+

On Fri, Aug 23, 2013 at 9:51 AM, Steve Hay <Steve.Hay@verosoftware.com> wrote:
> Hi Jan,
>
> Sorry to bother you again, but I was wondering if you'd had a chance 
> to give this any consideration?

Hi Steve,

sorry for being so slow in getting back to you! I'm always hoping to get some 
time to experiment with this myself, but this never actually happens...

[...]

> To avoid that, I imagine I would have to intercept any assignment to $!
> and do the WSAExxx -> Exxx mapping there, rather than in a function 
> only available to the core in win32sck.c as I have currently done. 
> Does that sound feasible/sensible?

Yes, I think this is an excellent idea!

> Finally, I also found (after searching for more hard-coded 100xy
> numbers) that IO::Socket contains a test for $! == 10022, which will 
> be broken with the new scheme because $! will now be set to 22 rather 
> than 10022. This is a nasty problem which I haven't yet got a plan for 
> how to solve. The Errno/POSIX value of EINVAL is and has always been 
> 22, so although testing against that will work nicely now, no existing 
> code will be using that because it would have needed to test for 10022 
> (which there is no named constant for).
>
> So to check for EINVAL one would have to write something like this:
>
>         if ($! == (($^O eq 'MSWin32' && $] < 5.019004) ? 10022 :
> EINVAL))
>
> which doesn't seem very friendly.

It *might* be possible to solve this with overload magic, but I'm afraid that 
this might require turning $! into a blessed reference (so that the overload 
code can find the right methods for performing the comparison).  It sounds a 
bit too convoluted to deal with just a few backward compatibility cases.

[...]

> I've pushed the patch that I sent you before, plus a couple of ugly 
> hacks to Net::Ping and IO::Socket to workaround the problems described 
> above (which both need sorting out properly somehow), to 
> smoke-me/steveh/errno. This branch passes all tests for me with VC10 
> and currently has four PASSes on linux at 
> http://perl.develop-help.com/?b=smoke-me/steveh/errno too.

I wanted to try it out, but still haven't gotten around to it yet.

One thing that I'm still wondering about is the stringification of errno 
values. If you look at win32_strerror, you'll see that we have special code to 
stringify errno values larger than sys_nerr via a call to FormatMessage() 
using the system message catalog.

So I wonder if strerror() in msvcrt.dll will provide the correct 
stringification for these additional error codes, especially on older 
operating systems.  I guess this is not an issue if you compile
*everything* with VC10, but I'm also thinking about mixing compilers for Perl 
itself and for the modules.

ActivePerl will be using a Mingw64 based compiler for 5.20 (same as 
Strawberry), so it will continue to link against msvcrt.dll. I haven't checked 
if the headers include the new E* constants, but if they do, will they work 
properly for the version of MSVCRT.dll available on WinXP?

Sorry for the rambling, haven't had time to properly organize my thoughts.

> Apologies for the long email, and thank you if you've read this far! 
> To summarize, my questions are:
>
> 1. Is the general approach taken ok?

Yes

> 2. Is it feasible/sensible to try to intercept all assignments to $! 
> to stop third-party code assigning WSAExxx values?

Yes, all $! assignments go through Perl_magic_set().  I was wondering if you 
should put the intercept into the SETERRNO() macro, but I don't think it is 
needed.  Only our socket code, or Perl level assignment to $! should need the 
translation.

> 3. Is the backwards-compatibility breakage regarding EINVAL and other 
> Exxx values < 100 acceptable for 5.20.0?

This is clearly a judgement call. I find it acceptable, given that leaving 
things as they are results in a bunch of broken code too.

Cheers,
-Jan

PS: I found the corresponding mapping function in the Tcl source, in case you 
want to take a look for comparison:

http://core.tcl.tk/tcl/artifact/055bdd9a7b71a8b90490e87da97623686d73b414

----------------------------------------------------------------------
From:	Steve Hay
Sent:	23 August 2013 17:52
To:	Jan Dubois
Subject:	RE: Problem with Winsock based Exxx constants in VS2010+

Hi Jan,

Sorry to bother you again, but I was wondering if you'd had a chance to give 
this any consideration?

I have since looked at this some more myself and found the cause of the Net-
Ping test failure. The problem is in its tcp_connect() function, which does 
this to fetch a socket-level error code:

	unpack("i", getsockopt($self->{"fh"}, SOL_SOCKET, SO_ERROR))

It assigns the result (which is a WSAExxx value) to $!, which now causes 
problems because in the new scheme of things we map WSAExxx values Exxx values 
for $! and accordingly define Errno's and POSIX's Exxx values as the errno.h 
values, hence the subsequent test for $! == ECONNREFUSED in ping_tcp() is now 
comparing 10061 to 140.

This could be an issue in other third-party code too: anyone can assign 
anything to $! (or errno in XS code), and until now people will have been used 
to assigning WSAExxx values (possibly from WSAGetLastError() or maybe using 
getsockopt() as above), but that's wrong now since Errno's and POSIX's Exxx 
values are now the errno.h values rather than winsock2.h values.

To avoid that, I imagine I would have to intercept any assignment to $! and do 
the WSAExxx -> Exxx mapping there, rather than in a function only available to 
the core in win32sck.c as I have currently done. Does that sound 
feasible/sensible?

I also noticed that Net/Ping.pm contains a bunch of hard-coded 100xy numbers, 
but they appear harmless: they are only used to set the likes of ECONNREFUSED 
if they are not already set, but AFAIK any remotely modern Perl will already 
have them (in this case, imported from POSIX).

Finally, I also found (after searching for more hard-coded 100xy numbers) that 
IO::Socket contains a test for $! == 10022, which will be broken with the new 
scheme because $! will now be set to 22 rather than 10022. This is a nasty 
problem which I haven't yet got a plan for how to solve. The Errno/POSIX value 
of EINVAL is and has always been 22, so although testing against that will 
work nicely now, no existing code will be using that because it would have 
needed to test for 10022 (which there is no named constant for).

So to check for EINVAL one would have to write something like this:

	if ($! == (($^O eq 'MSWin32' && $] < 5.019004) ? 10022 : EINVAL))

which doesn't seem very friendly.

This is really a problem with how things currently are (and have long been), 
namely that $! is being set to winsock2.h values, but if there is a 
corresponding errno.h value and it is < 100 then *that* is used for the 
Errno/POSIX constant, so authors have no good way of testing for these $! 
values. The new scheme corrects that by consistently using the same values for 
$! and for Errno/POSIX (except in the case of third-party code setting its own 
$!/errno values mentioned above), but nastily breaks backwards-compatibility.

(Ironically, Exxx values >= 100 (the ones which caused all this trouble in the 
first place!) don't have this problem as long as code uses the Exxx constants 
rather than the hard-coded 100xy numbers. For example, if code tests $! == 
ECONNABORTED, where $! and ECONNABORTED were both previously set to winsock2.h 
values then the same test will still work, except that now $! and ECONNABORTED 
are both set to errno.h values in VC10+ (or both left as winsock2.h values 
upto VC9). It does still break if any code is directly testing $! == 10053, 
but that's not such a problem: that's really broken code, which should be 
using the constant instead.)

I've pushed the patch that I sent you before, plus a couple of ugly hacks to 
Net::Ping and IO::Socket to workaround the problems described above (which 
both need sorting out properly somehow), to smoke-me/steveh/errno. This branch 
passes all tests for me with VC10 and currently has four PASSes on linux at 
http://perl.develop-help.com/?b=smoke-me/steveh/errno too.

Apologies for the long email, and thank you if you've read this far! To 
summarize, my questions are:

1. Is the general approach taken ok?
2. Is it feasible/sensible to try to intercept all assignments to $! to stop 
third-party code assigning WSAExxx values?
3. Is the backwards-compatibility breakage regarding EINVAL and other Exxx 
values < 100 acceptable for 5.20.0?

Thanks,
Steve


> -----Original Message-----
> From: Steve Hay
> Sent: 07 August 2013 22:55
> To: Jan Dubois
> Subject: RE: Problem with Winsock based Exxx constants in VS2010+
> 
> Hi Jan,
> 
> I've had a crack at doing this, and am attaching a patch (plus a new 
> file which the diff didn't pick up) of what I have so far to see if 
> you think I'm on the right lines before I spend any more time on it.
> 
> The main change is to use a new function throughout win32/win32sck.c 
> to convert WSAGetLastError() values into errno.h constants before 
> assigning to errno. I've mapped every possible WSAExxx value (as 
> documented by MSDN) to an Exxx value, but left the possibility of 
> other WSAxxx values cropping up and getting assigned to errno 
> unchanged because this has never been otherwise (and any non-existent 
> Exxx values get mapped back to WSAExxx values anyway...).
> 
> We must then ensure that all of those Exxx values are defined, which 
> is now done (in the new file, win32/include/sys/errno2.h) in a 
> friendlier manner, being careful not to redefine any (specifically the 
> new ones in
> VC++ 2010 and above) which already exist. The rest are defined as the
> WSAExxx values, as mentioned above.
> 
> Finally, we need the Errno module to know about these values, which 
> I've done by having it include that same new header file to ensure 
> that it gets the same definitions, rather than having to play its own games.
> I'm undecided whether that's a good move or not (and I'm uneasy about 
> my choice of location/name for the new file). What do you think? The 
> new header is also used in POSIX, which similarly wants definitions of 
> as many as possible of its hard-coded list of Exxx values, and I think 
> the change here is an improvement on the mass of duplication that 
> would otherwise have been involved since it didn't do its own mapping 
> as slickly as Errno did.
> 
> This builds successfully (and installing does install the new header 
> file, as required) and all tests pass except for one failure in Net- 
> Ping/t/450_service.t which I will look at in due course.
> 
> Any comments would be very welcome.
> 
> Thanks,
> Steve
> 
> 
> > -----Original Message-----
> > From: Jan Dubois
> > Sent: 31 July 2013 21:03
> > To: Steve Hay
> > Subject: Re: Problem with Winsock based Exxx constants in VS2010+
> >
> > Hi Steve,
> >
> > I think the proper way to deal with this will be to translate the
> WSA*
> > error codes to the corresponding errno values before storing them in 
> > errno.
> >
> > This may break modules with hard-coded errno values, but I think 
> > this will be unavoidable if we want to get to a sane state.  I think 
> > I had see some constants in autodie, but can't find it anymore.
> >
> > Changing the $! values is an incompatible change, so should probably 
> > not be done for 5.18.x, so I'm not sure where that leaves you with 
> > mod_perl.
> >
> > Let me know what you think, and if you are going to tackle the 
> > WSAGetLastError -> errno remapping, or if you want some help from me!
> >
> > Cheers,
> > -Jan





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