[comp.unix.ultrix] NFS & panic

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