marcl@ESD.3Com.COM (Marc Lavine) (03/13/91)
Well, last week I was having trouble using SIGIO inside Perl. I decided to rewrite my code to use select instead. That's when I discovered that the "recv" call doesn't work as documented. From looking at the code in eval.c, it seems obvious that recv does NOT grow the string which it is passed, potentially causing all manner of horrible results as it overwrites other data. I am not enclosing a patch for this. I switched to using "read" instead, which does grow the string passed to it. That's when I discovered this next problem. I found that if I created a lot of sockets, the "socket" call would start creating filehandles with no files attached. These would cause core dumps when I called fcntl on them. I tracked down the problem to the fact that there was no error checking on the fdopen calls in doio.c. In addition to returning bogus filehandles, Perl was also losing open file descriptors. I found similar problems in the code for the socketpair, accept, pipe, and open functions. I've enclosed a patch which I believe fixes these problems (which appear to have been around since at least version 3.0.37) and a test script which will demonstrate one of them. I hope that the powers that be will review these fixes and incorporate them into the official 4.0 release. Marc Lavine #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: socket.bug.pl doio.c.patch # Wrapped by marcl@cook.ESD.3Com.COM on Tue Mar 12 21:17:25 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'socket.bug.pl' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'socket.bug.pl'\" else echo shar: Extracting \"'socket.bug.pl'\" \(1252 characters\) sed "s/^X//" >'socket.bug.pl' <<'END_OF_FILE' X#!/usr/local/bin/perl X# X# Demonstrates a bug in Perl 4.0.beta: X# The "socket" call can appear to succeed even though fileno is bogus. X# X Xrequire "sys/socket.ph"; Xrequire "fcntl.ph"; X Xchop ( $ourhostname = `hostname` ); X$sockaddr = 'S n a4 x8'; X$host = "localhost"; X$port = "discard"; X X( $name, $aliases, $proto ) = getprotobyname ( "tcp" ); X( $name, $aliases, $rmtport ) = getservbyname ( $port, "tcp" ) X unless $port =~ /^\d+$/; X( $name, $aliases, $type, $len, $localaddr ) = X gethostbyname ( $ourhostname ); Xif ( !( ( $name, $aliases, $type, $len, $rmtaddr ) X = gethostbyname ( $host ) ) ) X{ X print ( "Can't get address for ", $host, "\n" ); X return; X} X X$sock = sprintf ( "SOCK.%d", $sock_counter++ ); X$localsock = pack ( $sockaddr, &AF_INET, 0, $localaddr ); X$rmtsock = pack ( $sockaddr, &AF_INET, $rmtport, $rmtaddr ); X Xwhile ( $result = socket ( $sock, &AF_INET, &SOCK_STREAM, $proto ) ) X{ X printf ( "Socket result=%d, file number=%d, counter=%d\n", X $result, fileno ( $sock ), $sock_counter ); X if ( !fileno ( $sock ) ) X { X # Cause a coredump. X fcntl ( $sock, &F_SETFL, &FNDELAY ) || die "fcntl: $!"; X } X X $sock = sprintf ( "SOCK.%d", $sock_counter++ ); X $localsock = pack ( $sockaddr, &AF_INET, 0, $localaddr ); X} END_OF_FILE if test 1252 -ne `wc -c <'socket.bug.pl'`; then echo shar: \"'socket.bug.pl'\" unpacked with wrong size! fi chmod +x 'socket.bug.pl' # end of 'socket.bug.pl' fi if test -f 'doio.c.patch' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'doio.c.patch'\" else echo shar: Extracting \"'doio.c.patch'\" \(2443 characters\) sed "s/^X//" >'doio.c.patch' <<'END_OF_FILE' X*** doio.c.dist Wed Feb 6 18:15:32 1991 X--- doio.c Tue Mar 12 18:49:31 1991 X*************** X*** 221,227 **** X else X fd = -1; X } X! fp = fdopen(dup(fd),mode); X } X else { X while (isspace(*name)) X--- 221,229 ---- X else X fd = -1; X } X! if (!(fp = fdopen(fd = dup(fd),mode))) { X! close(fd); X! } X } X else { X while (isspace(*name)) X*************** X*** 298,304 **** X if (stio->type != 's') X stio->ofp = fp; X else X! stio->ofp = fdopen(fileno(fp),"w"); X } X return TRUE; X } X--- 300,309 ---- X if (stio->type != 's') X stio->ofp = fp; X else X! if (!(stio->ofp = fdopen(fileno(fp),"w"))) { X! fclose(fp); X! stio->ifp = Nullfp; X! } X } X return TRUE; X } X*************** X*** 469,474 **** X--- 474,486 ---- X wstio->ifp = wstio->ofp; X rstio->type = '<'; X wstio->type = '>'; X+ if (!rstio->ifp || !wstio->ofp) { X+ if (rstio->ifp) fclose(rstio->ifp); X+ else close(fd[0]); X+ if (wstio->ofp) fclose(wstio->ofp); X+ else close(fd[1]); X+ goto badexit; X+ } X X str_sset(str,&str_yes); X return; X*************** X*** 1304,1309 **** X--- 1316,1327 ---- X stio->ifp = fdopen(fd, "r"); /* stdio gets confused about sockets */ X stio->ofp = fdopen(fd, "w"); X stio->type = 's'; X+ if (!stio->ifp || !stio->ofp) { X+ if (stio->ifp) fclose(stio->ifp); X+ if (stio->ofp) fclose(stio->ofp); X+ if (!stio->ifp && !stio->ofp) close(fd); X+ return FALSE; X+ } X X return TRUE; X } X*************** X*** 1426,1431 **** X--- 1444,1455 ---- X nstio->ifp = fdopen(fd, "r"); X nstio->ofp = fdopen(fd, "w"); X nstio->type = 's'; X+ if (!nstio->ifp || !nstio->ofp) { X+ if (nstio->ifp) fclose(nstio->ifp); X+ if (nstio->ofp) fclose(nstio->ofp); X+ if (!nstio->ifp && !nstio->ofp) close(fd); X+ goto badexit; X+ } X X str_nset(str, buf, len); X return; X*************** X*** 1990,1995 **** X--- 2014,2028 ---- X stio2->ifp = fdopen(fd[1], "r"); X stio2->ofp = fdopen(fd[1], "w"); X stio2->type = 's'; X+ if (!stio1->ifp || !stio1->ofp || !stio2->ifp || !stio2->ofp) { X+ if (stio1->ifp) fclose(stio1->ifp); X+ if (stio1->ofp) fclose(stio1->ofp); X+ if (!stio1->ifp && !stio1->ofp) close(fd[0]); X+ if (stio2->ifp) fclose(stio2->ifp); X+ if (stio2->ofp) fclose(stio2->ofp); X+ if (!stio2->ifp && !stio2->ofp) close(fd[1]); X+ return FALSE; X+ } X X return TRUE; X } END_OF_FILE if test 2443 -ne `wc -c <'doio.c.patch'`; then echo shar: \"'doio.c.patch'\" unpacked with wrong size! fi # end of 'doio.c.patch' fi echo shar: End of shell archive. exit 0 -- Marc Lavine Broken: marcl%3Com.Com@sun.com Smart: marcl@3Com.Com UUCP: ...{sun|decwrl}!3Com.Com!marcl
lwall@jpl-devvax.jpl.nasa.gov (Larry Wall) (03/13/91)
In article <marcl.668838671@cook.ESD.3Com.COM> marcl@ESD.3Com.COM (Marc Lavine) writes: : Well, last week I was having trouble using SIGIO inside Perl. I : decided to rewrite my code to use select instead. That's when I : discovered that the "recv" call doesn't work as documented. From : looking at the code in eval.c, it seems obvious that recv does NOT : grow the string which it is passed, potentially causing all manner of : horrible results as it overwrites other data. I am not enclosing a : patch for this. I switched to using "read" instead, which does grow : the string passed to it. : : That's when I discovered this next problem. I found that if I created : a lot of sockets, the "socket" call would start creating filehandles : with no files attached.... : : I hope that the powers that be will review these fixes and incorporate : them into the official 4.0 release. They're fixed now, including recv. Thanks. Tonight I finally hacked in timelocal.pl, like the book sez is supposed to be there. Pretty much all I have left is to futz with metaconfig some more, and 4.0 will be there. Doubtless I've totally busted it somehow that won't show up till I release it... :-) Larry P.S. For the curious, here's timelocal.pl. Comments welcome. ;# timelocal.pl ;# ;# Usage: ;# $time = timelocal($sec,$min,$hours,$mday,$mon,$year,$junk,$junk,$isdst); ;# $time = timegm($sec,$min,$hours,$mday,$mon,$year); ;# These routines are quite efficient and yet are always guaranteed to agree ;# with localtime() and gmtime(). We manage this by caching the start times ;# of any months we've seen before. If we know the start time of the month, ;# we can always calculate any time within the month. The start times ;# themselves are guessed by successive approximation starting at the ;# current time, since most dates seen in practice are close to the ;# current date. Unlike algorithms that do a binary search (calling gmtime ;# once for each bit of the time value, resulting in 32 calls), this algorithm ;# calls it at most 6 times, and usually only once or twice. If you hit ;# the month cache, of course, it doesn't call it at all. ;# timelocal is implemented using the same cache. We just assume that we're ;# translating a GMT time, and then fudge it when we're done for the timezone ;# and daylight savings arguments. The timezone is determined by examining ;# the result of localtime(0) when the package is initialized. The daylight ;# savings offset is currently assumed to be one hour. CONFIG: { package timelocal; @epoch = localtime(0); $tzmin = $epoch[2] * 60 + $epoch[1]; # minutes east of GMT if ($tzmin > 0) { $tzmin = 24 * 60 - $tzmin; # minutes west of GMT $tzmin -= 24 * 60 if $epoch[5] == 70; # account for the date line } $SEC = 1; $MIN = 60 * $SEC; $HR = 60 * $MIN; $DAYS = 24 * $HR; } sub timegm { package timelocal; $ym = pack(C2, @_[5,4]); $cheat = $cheat{$ym} || &cheat; $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAYS; } sub timelocal { package timelocal; $ym = pack(C2, @_[5,4]); $cheat = $cheat{$ym} || &cheat; $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAYS + $tzmin * $MIN - 60 * 60 * ($_[8] != 0); } package timelocal; sub cheat { $year = $_[5]; $month = $_[4]; $guess = $^T; @g = gmtime($guess); while ($diff = $year - $g[5]) { $guess += $diff * (364 * $DAYS); @g = gmtime($guess); } while ($diff = $month - $g[4]) { $guess += $diff * (28 * $DAYS); @g = gmtime($guess); } $g[3]--; $guess -= $g[0] * $SEC + $g[1] * $MIN + $g[2] * $HR + $g[3] * $DAYS; $cheat{$ym} = $guess; }