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!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!