cck@cunixc.columbia.edu (Charlie C. Kim) (12/17/87)
This one is pretty annoying. If you know what you are doing, you can patch the running or binary kernel by modifying the 2 bytes at vop_create+0xa1 from 0x911 to 0x1311 (make jump to second gput at +b6 instead of first at +ac). Charlie C. Kim User Services Columbia University DATE: 12/09/87 OPERATING SYSTEM: Ultrix 2.0-1 PRIORITY: Medium-rare or so REPORTED BY: Charlie C. Kim, User Services, Columbia University PROBLEM: A client creating a file on a Ultrix 2.0-1 NFS server can cause the server to crash with a "gfs_unlock: unlocked gnode". DIAGNOSIS: nfs/vnodeops_gfs.c&&vop_create calls GMAKNODE with ndp->ni_pdir set and locked by GNAMEI due to the CREATE & LOCKPARENT flags in ndp->ni_nameiop. Upon successful exit of GMAKNODE, the gnode in ndp->ni_pdir has been unlocked by a call to gput (in direnter to be more specific). Following the call to GMAKNODE, there is code which checks vap->va_size (comes from the rpc arguments). This code may jump to error recovery code that does a gput on the gnode in dgp (dgp is a copy of ndp->ni_pdir). Since the gnode pointed to by ndp->ni_pdir/dgp has already been unlocked (already gput), the call to gput will cause a panic when gput attempts to unlock that gnode. Most NFS client implementations do not tickle this bug. FIX: Apply the following patch. !START OF PATCH!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! *** /tmp/,RCSt1008616 Wed Dec 9 13:49:39 1987 --- vnodeops_gfs.c Wed Dec 9 13:35:20 1987 *************** *** 352,358 return (u.u_error); if ((vap->va_size != 0) && (vap->va_size != -1)) { u.u_error = EINVAL; /* ??? */ ! goto bad; } } else { if (exclusive) { --- 352,359 ----- return (u.u_error); if ((vap->va_size != 0) && (vap->va_size != -1)) { u.u_error = EINVAL; /* ??? */ ! /* cck: dgp is not locked: c.f. ufs_maknode&direnter */ ! goto bad2; /* cck */ } } else { if (exclusive) { *************** *** 382,388 return(u.u_error); bad: gput(dgp); ! gput(*gpp); *gpp = NULL; return (u.u_error); } --- 383,389 ----- return(u.u_error); bad: gput(dgp); ! bad2: gput(*gpp); /* cck */ *gpp = NULL; return (u.u_error); } !END OF PATCH!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!