Hi,

I have seen a crash in the regress/sys/nfs test.  It was during
run-regress-cleanup, the last command I see is:

umount -f -t nfs -h 127.0.0.1 -a || true

uvm_fault(0xd0d2f2a8, 0xefff0000, 0, 1) -> e
kernel: page fault trap, code=0
Stopped at      nfs_timer+0x30: cmpl    $0,0xc(%ebx)

ddb{0}> trace
nfs_timer(d631c300) at nfs_timer+0x30
softclock_thread(d570a444) at softclock_thread+0xc7

ddb{0}> ps
   PID     TID   PPID    UID  S       FLAGS  WAIT          COMMAND
*89864  371985      0      0  7  0x40014200                softclock

ddb{0}> show register
ds                  0x10
es                  0x10
fs                  0x20
gs                     0
edi           0xd631c300        end+0x5521300
esi           0xd0601600        nfs_timer
ebp           0xf52d7e7c
ebx           0xefff0017
edx                  0x1
ecx           0xd570a444        end+0x490f444
eax                    0
eip           0xd0601630        nfs_timer+0x30
cs                   0x8
eflags           0x10286        __ALIGN_SIZE+0xf286
esp           0xf52d7e6c
ss                  0x10
nfs_timer+0x30: cmpl    $0,0xc(%ebx)

The struct nfsmount *nmp passed as argument to nfs_timer() has been
freed.
ddb{0}> x/x 0xd631c300,8 
0xd631c300:     efffeecc
0xd631c304:     efff0017
0xd631c308:     ba65fc7c
0xd631c30c:     efffeecc
0xd631c310:     efffeecc
0xd631c314:     efffeecc
0xd631c318:     efffeecc
0xd631c31c:     efffeecc

%ebx contains the bad value from nmp->nm_reqsq.
00001af0 <nfs_timer>:
/usr/src/sys/nfs/nfs_socket.c:1138
*   1b20:       83 7b 0c 00             cmpl   $0x0,0xc(%ebx)
    1b24:       0f 85 f6 02 00 00       jne    1e20 <nfs_timer+0x330>
    1b2a:       8b 4b 24                mov    0x24(%ebx),%ecx
    1b2d:       f6 c1 04                test   $0x4,%cl
    1b30:       0f 85 ea 02 00 00       jne    1e20 <nfs_timer+0x330>
/usr/src/sys/nfs/nfs_socket.c:1140

When accessing nmp->nm_reqsq->r_mrep it crashes.
nfs/nfs_socket.c
  1136          NET_LOCK();
  1137          TAILQ_FOREACH(rep, &nmp->nm_reqsq, r_chain) {
* 1138                  if (rep->r_mrep || (rep->r_flags & R_SOFTTERM))
  1139                          continue;

I guess that the timeout is run after the nfs unmount.  Maybe it
was sleeping in NET_LOCK().  We should delay the free(9) of struct
nfsmount after all timeouts are run.  That is how we solve the issue
elsewhere.

ok?

bluhm

Index: nfs/nfs_vfsops.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/nfs/nfs_vfsops.c,v
retrieving revision 1.119
diff -u -p -r1.119 nfs_vfsops.c
--- nfs/nfs_vfsops.c    2 May 2018 02:24:56 -0000       1.119
+++ nfs/nfs_vfsops.c    3 May 2018 18:46:33 -0000
@@ -84,6 +84,7 @@ int   nfs_start(struct mount *, int, struc
 int    nfs_statfs(struct mount *, struct statfs *, struct proc *);
 int    nfs_sync(struct mount *, int, int, struct ucred *, struct proc *);
 int    nfs_unmount(struct mount *, int, struct proc *);
+void   nfs_reaper(void *);
 int    nfs_vget(struct mount *, ino_t, struct vnode **);
 int    nfs_vptofh(struct vnode *, struct fid *);
 int    nfs_mountroot(void);
@@ -746,9 +747,21 @@ nfs_unmount(struct mount *mp, int mntfla
        nfs_disconnect(nmp);
        m_freem(nmp->nm_nam);
        timeout_del(&nmp->nm_rtimeout);
-       free(nmp, M_NFSMNT, sizeof(*nmp));
+       timeout_set_proc(&nmp->nm_rtimeout, nfs_reaper, nmp);
+       timeout_add(&nmp->nm_rtimeout, 0);
        mp->mnt_data = NULL;
        return (0);
+}
+
+/*
+ * Delay nfs mount point free until pending or sleeping timeouts have finished.
+ */
+void
+nfs_reaper(void *arg)
+{
+       struct nfsmount *nmp = arg;
+
+       free(nmp, M_NFSMNT, sizeof(*nmp));
 }
 
 /*

Reply via email to