Subject: | [FORWARD] Socket.xs does not handle correctly UNIX domain sockets on FreeBSD |
https://rt.perl.org/rt3/Ticket/Display.html?id=67298
The patch is very obsolete but still fails for me on FreeBSD 9.1:
Server:
$ ~/perl/v5.19.1-127-g39150c4/bin/perl -v
This is perl 5, version 19, subversion 2 (v5.19.2 (v5.19.1-127-g39150c4)) built for i386-freebsd
...
$ ~/perl/v5.19.1-127-g39150c4/bin/perl rt67298-server.pl mysock
(blocks, which is good)
Client:
$ ~/perl/v5.19.1-127-g39150c4/bin/perl rt67298-client.pl mysock
*** Connecting to mysock
hexdump: <08><01>mysock--
real length: 8
sun_len field: 8
Bad arg length for Socket::unpack_sockaddr_un, length is 8, should be 106 at rt67298-client.pl line 19.
This is a bug report for perl from btik-fbsd@scoubidou.com,
generated with the help of perlbug 1.36 running under perl 5.10.0.
Bug with UNIX domain sockets on FreeBSD (at least 7.0 until 7.2
RELEASE).
The unpack_sockaddr_un of perl-5.10.0/ext/Socket/Socket.xs does not
handle correctly struct sockaddr_un under FreeBSD in some cases.
It returns errors like:
Bad arg length for Socket::unpack_sockaddr_un, length is 16,
should be 106 at ...
When the system returns a struct sockaddr_un in getpeername(), it
returns the same structure the server gives to bind().
In some cases, servers (X for /tmp/.X11-unix/X0 and devd for
/var/run/devd.pipe, for example) give a shortest structure than
sizeof(struct sockaddr_un) but with a correct sun_len field. In these
cases, sun_path may not be '\0' terminated.
Then getpeername() returns a structure that Socket.xs
unpack_sockaddr_un() function think wrong sized. But it is not.
One can reproduce the problem with the following simple server and
client code.
Server side :
---------------------------------------
use strict;
use Socket;
my $name = $ARGV[0] or die "usage: $0 server_socket_file";
#my $sun = sockaddr_un($name);
# Use a shortest sockaddr_un as FreeBSD allows it for its daemons
my $sun = pack('CCA*', length($name)+2, 1, $name);
socket(my $fh, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!\n";
bind($fh, $sun) or die "bind: $!\n";
listen($fh, 1) or die "listen: $!\n";
while (1)
{
accept(my $new_fh, $fh) or die "accept: $!\n";
sleep 1;
}
---------------------------------------
Client side (note that /var/run/devd.pipe socket is always available
on standard FreeBSD installation):
---------------------------------------
use strict;
use Socket;
my $name = $ARGV[0] // '/var/run/devd.pipe';
warn "*** Connecting to $name\n";
my $sun = sockaddr_un($name);
socket(my $fh, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!\n";
connect($fh, $sun) or die "connect: $!\n";
my $sockaddr = getpeername($fh) or die "getpeername: $!\n";
my $warn;
($warn = $sockaddr) =~ s/([\x00-\x19\x7f-\xff])/sprintf("<%02x>",ord$1)/ge;
warn "hexdump: $warn--\n";
warn "real length: " . length($sockaddr) . "\n";
warn "sun_len field: " . unpack('C', $sockaddr) . "\n";
my $file = unpack_sockaddr_un($sockaddr);
warn "unpack: $file--\n";
close $fh;
---------------------------------------
I join a patch that correct the problem on FreeBSD.
Note that the version in the GIT repository have the same problem.
Perhaps the same problem occurs on other BSD (Open, Net, DragonFly),
but I don't have any of them to try...
Many thanks for your work, dont hesitate to contact me to do some
tests if you need...
Best regards,
Maxime Soulé.
Patch to perl-5.10.0/ext/Socket/Socket.xs :
--- perl-5.10.0/ext/Socket/Socket.xs.orig 2009-07-07 17:29:59.000000000 +0200
+++ perl-5.10.0/ext/Socket/Socket.xs 2009-07-07 18:25:57.000000000 +0200
@@ -351,19 +351,25 @@
#ifdef I_SYS_UN
struct sockaddr_un addr;
STRLEN sockaddrlen;
+ STRLEN sockaddrmaxlen;
char * sun_ad = SvPVbyte(sun_sv,sockaddrlen);
char * e;
+# ifdef __FreeBSD__
+ sockaddrmaxlen = addr.sun_len;
+# else
+ sockaddrmaxlen = sizeof(addr);
+# endif
# ifndef __linux__
/* On Linux sockaddrlen on sockets returned by accept, recvfrom,
getpeername and getsockname is not equal to sizeof(addr). */
- if (sockaddrlen != sizeof(addr)) {
+ if (sockaddrlen != sockaddrmaxlen) {
croak("Bad arg length for %s, length is %d, should be %d",
"Socket::unpack_sockaddr_un",
- sockaddrlen, sizeof(addr));
+ sockaddrlen, sockaddrmaxlen);
}
# endif
- Copy( sun_ad, &addr, sizeof addr, char );
+ Copy( sun_ad, &addr, sockaddrmaxlen, char );
if ( addr.sun_family != AF_UNIX ) {
croak("Bad address family for %s, got %d, should be %d",
@@ -372,11 +378,18 @@
AF_UNIX);
}
e = (char*)addr.sun_path;
+# ifdef __FreeBSD__
+ /* On FreeBSD sun_path ends not always with a '\0'.
+ * How do other BSDs work? */
+ while (e < (char*)&addr + sockaddrmaxlen && *e)
+ ++e;
+# else
/* On Linux, the name of abstract unix domain sockets begins
* with a '\0', so allow this. */
while ((*e || (e == addr.sun_path && e[1] && sockaddrlen > 1))
&& e < (char*)addr.sun_path + sizeof addr.sun_path)
++e;
+# endif
ST(0) = sv_2mortal(newSVpvn(addr.sun_path, e - (char*)addr.sun_path));
#else
ST(0) = (SV *) not_here("unpack_sockaddr_un");
---
Flags:
category=library
severity=medium
---
Site configuration information for perl 5.10.0:
Configured by max at Wed Jun 10 09:50:59 CEST 2009.
Summary of my perl5 (revision 5 version 10 subversion 0) configuration:
Platform:
osname=freebsd, osvers=7.2-release, archname=amd64-freebsd-thread-multi
uname='freebsd auber.mobigard.com 7.2-release freebsd 7.2-release #1: mon may 4 19:16:51 cest 2009 max@auber.mobigard.com:usrobjusrsrcsysauber amd64 '
config_args='-sde -Dprefix=/usr/local -Darchlib=/usr/local/lib/perl5/5.10.0/mach -Dprivlib=/usr/local/lib/perl5/5.10.0 -Dman3dir=/usr/local/lib/perl5/5.10.0/perl/man/man3 -Dman1dir=/usr/local/man/man1 -Dsitearch=/usr/local/lib/perl5/site_perl/5.10.0/mach -Dsitelib=/usr/local/lib/perl5/site_perl/5.10.0 -Dscriptdir=/usr/local/bin -Dsiteman3dir=/usr/local/lib/perl5/5.10.0/man/man3 -Dsiteman1dir=/usr/local/man/man1 -Ui_malloc -Ui_iconv -Uinstallusrbinperl -Dcc=cc -Duseshrplib -Dinc_version_list=none -Dccflags=-DAPPLLIB_EXP="/usr/local/lib/perl5/5.10.0/BSDPAN" -Doptimize=-O2 -fno-strict-aliasing -pipe -march=nocona -Ud_dosuid -Ui_gdbm -Dusethreads=y -Dusemymalloc=n -Duse64bitint'
hint=recommended, useposix=true, d_sigaction=define
useithreads=define, usemultiplicity=define
useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
use64bitint=define, use64bitall=define, uselongdouble=undef
usemymalloc=n, bincompat5005=undef
Compiler:
cc='cc', ccflags ='-DAPPLLIB_EXP="/usr/local/lib/perl5/5.10.0/BSDPAN" -DHAS_FPSETMASK -DHAS_FLOATINGPOINT_H -fno-strict-aliasing -pipe -I/usr/local/include',
optimize='-O2 -fno-strict-aliasing -pipe -march=nocona',
cppflags='-DAPPLLIB_EXP="/usr/local/lib/perl5/5.10.0/BSDPAN" -DHAS_FPSETMASK -DHAS_FLOATINGPOINT_H -fno-strict-aliasing -pipe -I/usr/local/include'
ccversion='', gccversion='4.2.1 20070719 [FreeBSD]', gccosandvers=''
intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
alignbytes=8, prototype=define
Linker and Libraries:
ld='cc', ldflags ='-pthread -Wl,-E -L/usr/local/lib'
libpth=/usr/lib /usr/local/lib
libs=-lgdbm -lm -lcrypt -lutil
perllibs=-lm -lcrypt -lutil
libc=, so=so, useshrplib=true, libperl=libperl.so
gnulibc_version=''
Dynamic Linking:
dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags=' -Wl,-R/usr/local/lib/perl5/5.10.0/mach/CORE'
cccdlflags='-DPIC -fPIC', lddlflags='-shared -L/usr/local/lib'
Locally applied patches:
---
@INC for perl 5.10.0:
/usr/local/lib/perl5/5.10.0/BSDPAN
/usr/local/lib/perl5/site_perl/5.10.0/mach
/usr/local/lib/perl5/site_perl/5.10.0
/usr/local/lib/perl5/5.10.0/mach
/usr/local/lib/perl5/5.10.0
.
---
Environment for perl 5.10.0:
HOME=/home/max
LANG=fr_FR.ISO8859-1
LANGUAGE (unset)
LD_LIBRARY_PATH (unset)
LOGDIR (unset)
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/usr/local/sbin
PERL_BADLANG (unset)
SHELL=/usr/local/bin/zsh