eichin@athena.mit.edu (Mark W. Eichin) (12/15/90)
I've run across a few things that the perl self-tests don't notice, and I'd like to know whether these things fall under "undefined behavior" or are real bugs. Since I've found what I think is a fix, I'd like to call them bugs :-) I've narrowed a larger problem down to the following example. ./perl in this case is pl41, under Ultrix 3.1, castneg=undef, waitpid=undef, built with gcc-osf-1.9.2.13. Note that this version of perl *passes* make test: All tests successful. u=1.06667 s=2.06667 cu=7.25 cs=12.6667 Much reduced example: % ./perl -e ' printf "%02x " x 4, unpack("CCCC",pack("N",0x8000000f));' 7f ff ff ff substitute any value less than 0x80000000 and it prints the expected byte stream. HOWEVER: % ./perl -e ' printf "%02x " x 4, unpack("CCCC",pack("N",(1<<31)+1));' 80 00 00 01 works. (No, I don't fully understand why it works, yet.) Adding some debug statements to doarg.c:do_pack determined that this compiler and architecture do range checking on double->long conversions, and that doarg.c,579: along = (long)str_gnum(fromstr); if str_gnum (which comes out a double?) is greater than 0x7fffffff, along is set to 0x7fffffff. In fact, the debug statement I added (when combined with -D8) prints: /perl -D2056 -e ' printf "%02x " x 4, unpack("CCCC",pack("N",0xf000000f));' [some lines elided] 1 E! P: L: U: 1.SINGLE = 'CCCC' 1 E! P: L: U: 2.EXPR => 1 E! P: L: U: PACK (1001c388) 2 args: 1 E! P: L: U: P: 1.SINGLE = 'N' 1 E! P: L: U: P: 2.EXPR => 1 E! P: L: U: P: LIST (1001c448) 1 args: 1 E! P: L: U: P: L: 1.SINGLE = 'num(4.02653e+09)' 1 E! P: L: U: P: LIST RETURNS ("4026531855") 1 E! P: L: U: P: 2.EXPR = '4026531855' 1 E! P: L: U: P: pack(N) 2: str = 4026531855.000000, along = 7fffffff 1 E! P: L: U: PACK RETURNS "" 1 E! P: L: U: 2.EXPR = '' 1 E! P: L: UNPACK RETURNS 4 ARGS ("127",...,"255") .. 4026531855 is actually 0xf000000f, but the assignment forces it down to MAXLONG. On this architecture, changing the (long) to (unsigned long) is safe, since casting of negative floats to unsigned long preserves the bit pattern. Looking at the places castneg=undef propagates to, and noting the #ifdef mips that's already in util.c, and openly making the assumption that htonl doesn't care if it's arguments are signed or unsigned, I'd suggest the following patch (after which it still passes all tests): *** doarg.c Sat Dec 15 05:47:32 1990 --- ../../src/perl/doarg.c Tue Dec 11 02:18:51 1990 *************** *** 577,587 **** case 'N': while (len-- > 0) { fromstr = NEXTFROM; ! aulong = U_L(str_gnum(fromstr)); #ifdef HTONL ! aulong = htonl(aulong); #endif ! str_ncat(str,(char*)&aulong,sizeof(unsigned long)); } break; case 'L': --- 577,587 ---- case 'N': while (len-- > 0) { fromstr = NEXTFROM; ! along = (long)str_gnum(fromstr); #ifdef HTONL ! along = htonl(along); #endif ! str_ncat(str,(char*)&along,sizeof(long)); } break; case 'L': _Mark_ <eichin@athena.mit.edu>