Bring in fix from FreeBSD by tjr, 3 years and 9 months ago: Store a

reference to the parent directory's vnode instead of its smbnode to
avoid a use-after-free bug causing a panic when a smbfs mount is
forcefully unmounted.

Keep trying to flush the vnode list for the mount while some are still
busy and we are making progress towards making them not busy.  This
stops attempts to unmount idle smbfs mounts failing with EBUSY.

The easiest way to reproduce the above problem, from what I have seen is:
1) Assume /s is a smbfs mount point.
2) mount /s
3) stat /s/foo/1
4) umount /s
   Returns error because the file system is busy.
5) Shutdown the machine: panic in smbfs_reclaim because vrele
   accesses already-released memory.
This commit is contained in:
jmmv 2006-11-02 17:34:21 +00:00
parent c2d79bf895
commit e0fd5659c6
8 changed files with 46 additions and 29 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: smbfs.h,v 1.14 2006/05/14 21:31:52 elad Exp $ */
/* $NetBSD: smbfs.h,v 1.15 2006/11/02 17:34:21 jmmv Exp $ */
/*
* Copyright (c) 2000-2001, Boris Popov
@ -85,6 +85,7 @@ struct smbmount {
struct lock sm_hashlock;
LIST_HEAD(smbnode_hashhead, smbnode) *sm_hash;
u_long sm_hashlen;
int sm_didrele;
};
#define VFSTOSMBFS(mp) ((struct smbmount *)((mp)->mnt_data))

View File

@ -1,4 +1,4 @@
/* $NetBSD: smbfs_io.c,v 1.23 2006/05/14 21:31:52 elad Exp $ */
/* $NetBSD: smbfs_io.c,v 1.24 2006/11/02 17:34:21 jmmv Exp $ */
/*
* Copyright (c) 2000-2001, Boris Popov
@ -36,7 +36,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: smbfs_io.c,v 1.23 2006/05/14 21:31:52 elad Exp $");
__KERNEL_RCSID(0, "$NetBSD: smbfs_io.c,v 1.24 2006/11/02 17:34:21 jmmv Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -121,7 +121,7 @@ smbfs_readvdir(struct vnode *vp, struct uio *uio, kauth_cred_t cred)
/* Simulate .. */
if (limit > 0 && offset < 2) {
memset(&de, 0, sizeof(de));
de.d_fileno = (np->n_parent ? np->n_parent->n_ino : 2);
de.d_fileno = (np->n_parent ? VTOSMB(np->n_parent)->n_ino : 2);
de.d_reclen = DE_SIZE;
de.d_type = DT_DIR;
de.d_namlen = 2;

View File

@ -1,4 +1,4 @@
/* $NetBSD: smbfs_node.c,v 1.29 2006/07/23 22:06:10 ad Exp $ */
/* $NetBSD: smbfs_node.c,v 1.30 2006/11/02 17:34:21 jmmv Exp $ */
/*
* Copyright (c) 2000-2001 Boris Popov
@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: smbfs_node.c,v 1.29 2006/07/23 22:06:10 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: smbfs_node.c,v 1.30 2006/11/02 17:34:21 jmmv Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -113,7 +113,7 @@ smbfs_node_alloc(struct mount *mp, struct vnode *dvp,
if (nmlen == 2 && memcmp(name, "..", 2) == 0) {
if (dvp == NULL)
return EINVAL;
vp = VTOSMB(dvp)->n_parent->n_vnode;
vp = VTOSMB(VTOSMB(dvp)->n_parent)->n_vnode;
if ((error = vget(vp, LK_EXCLUSIVE | LK_RETRY)) == 0)
*vpp = vp;
return (error);
@ -130,7 +130,7 @@ retry:
loop:
nhpp = SMBFS_NOHASH(smp, hashval);
LIST_FOREACH(np, nhpp, n_hash) {
if (np->n_parent != dnp
if (np->n_parent != dvp
|| np->n_nmlen != nmlen
|| memcmp(name, np->n_name, nmlen) != 0)
continue;
@ -171,7 +171,7 @@ loop:
KASSERT(vp->v_type != VREG || dvp != NULL);
if (dvp) {
np->n_parent = dnp;
np->n_parent = dvp;
if (/*vp->v_type == VDIR &&*/ (dvp->v_flag & VROOT) == 0) {
vref(dvp);
np->n_flag |= NREFPARENT;
@ -186,7 +186,7 @@ loop:
* malloc.
*/
LIST_FOREACH(np2, nhpp, n_hash) {
if (np2->n_parent != dnp
if (np2->n_parent != dvp
|| np2->n_nmlen != nmlen
|| memcmp(name, np2->n_name, nmlen) != 0)
continue;
@ -246,7 +246,7 @@ smbfs_reclaim(v)
smbfs_hash_lock(smp);
dvp = (np->n_parent && (np->n_flag & NREFPARENT)) ?
np->n_parent->n_vnode : NULL;
np->n_parent : NULL;
LIST_REMOVE(np, n_hash);
@ -260,8 +260,14 @@ smbfs_reclaim(v)
if (np->n_name)
smbfs_name_free(np->n_name);
pool_put(&smbfs_node_pool, np);
if (dvp)
if (dvp) {
vrele(dvp);
/*
* Indicate that we released something; see comment
* in smbfs_unmount().
*/
smp->sm_didrele = 1;
}
return 0;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: smbfs_node.h,v 1.11 2006/05/14 21:31:52 elad Exp $ */
/* $NetBSD: smbfs_node.h,v 1.12 2006/11/02 17:34:21 jmmv Exp $ */
/*
* Copyright (c) 2000-2001, Boris Popov
@ -56,7 +56,7 @@ struct smbfs_fctx;
struct smbnode {
struct genfs_node n_gnode;
int n_flag;
struct smbnode * n_parent;
struct vnode * n_parent;
struct vnode * n_vnode;
struct smbmount * n_mount;
time_t n_attrage; /* attributes cache time */

View File

@ -1,4 +1,4 @@
/* $NetBSD: smbfs_smb.c,v 1.32 2006/10/12 01:32:14 christos Exp $ */
/* $NetBSD: smbfs_smb.c,v 1.33 2006/11/02 17:34:21 jmmv Exp $ */
/*-
* Copyright (c) 2003 The NetBSD Foundation, Inc.
@ -71,7 +71,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: smbfs_smb.c,v 1.32 2006/10/12 01:32:14 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: smbfs_smb.c,v 1.33 2006/11/02 17:34:21 jmmv Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -1317,7 +1317,8 @@ smbfs_smb_lookup(struct smbnode *dnp, const char *name, int nmlen,
error = smbfs_smb_lookup(dnp, NULL, 0, fap, scred);
return error;
} else if (nmlen == 2 && name[0] == '.' && name[1] == '.') {
error = smbfs_smb_lookup(dnp->n_parent, NULL, 0, fap, scred);
error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0,
fap, scred);
printf("%s: knows NOTHING about '..'\n", __func__);
return error;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: smbfs_subr.c,v 1.11 2006/10/12 01:32:14 christos Exp $ */
/* $NetBSD: smbfs_subr.c,v 1.12 2006/11/02 17:34:21 jmmv Exp $ */
/*
* Copyright (c) 2000-2001, Boris Popov
@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: smbfs_subr.c,v 1.11 2006/10/12 01:32:14 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: smbfs_subr.c,v 1.12 2006/11/02 17:34:21 jmmv Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -276,7 +276,7 @@ smb_fphelp(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *np,
return ENAMETOOLONG;
}
*npp++ = np;
np = np->n_parent;
np = VTOSMB(np->n_parent);
}
while (i--) {
np = *--npp;

View File

@ -1,4 +1,4 @@
/* $NetBSD: smbfs_vfsops.c,v 1.58 2006/10/25 22:01:54 reinoud Exp $ */
/* $NetBSD: smbfs_vfsops.c,v 1.59 2006/11/02 17:34:21 jmmv Exp $ */
/*
* Copyright (c) 2000-2001, Boris Popov
@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: smbfs_vfsops.c,v 1.58 2006/10/25 22:01:54 reinoud Exp $");
__KERNEL_RCSID(0, "$NetBSD: smbfs_vfsops.c,v 1.59 2006/11/02 17:34:21 jmmv Exp $");
#ifdef _KERNEL_OPT
#include "opt_quota.h"
@ -252,9 +252,17 @@ smbfs_unmount(struct mount *mp, int mntflags, struct lwp *l)
smp->sm_root = NULL;
}
/* Flush all vnodes. */
if ((error = vflush(mp, NULLVP, flags)) != 0)
return error;
/* Flush all vnodes.
* Keep trying to flush the vnode list for the mount while
* some are still busy and we are making progress towards
* making them not busy. This is needed because smbfs vnodes
* reference their parent directory but may appear after their
* parent in the list; one pass over the vnode list is not
* sufficient in this case. */
do {
smp->sm_didrele = 0;
error = vflush(mp, NULLVP, flags);
} while (error == EBUSY && smp->sm_didrele != 0);
smb_makescred(&scred, l, l->l_cred);
smb_share_lock(smp->sm_share, 0);

View File

@ -1,4 +1,4 @@
/* $NetBSD: smbfs_vnops.c,v 1.54 2006/10/12 01:32:14 christos Exp $ */
/* $NetBSD: smbfs_vnops.c,v 1.55 2006/11/02 17:34:21 jmmv Exp $ */
/*-
* Copyright (c) 2003 The NetBSD Foundation, Inc.
@ -71,7 +71,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: smbfs_vnops.c,v 1.54 2006/10/12 01:32:14 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: smbfs_vnops.c,v 1.55 2006/11/02 17:34:21 jmmv Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -900,7 +900,7 @@ smbfs_print(v)
printf("tag VT_SMBFS, name = %.*s, parent = %p, open = %d\n",
(int)np->n_nmlen, np->n_name,
np->n_parent ? SMBTOV(np->n_parent) : NULL,
np->n_parent ? np->n_parent : NULL,
(np->n_flag & NOPEN) != 0);
printf(" ");
lockmgr_printinfo(vp->v_vnlock);
@ -1308,7 +1308,8 @@ smbfs_lookup(v)
*/
smb_makescred(&scred, cnp->cn_lwp, cnp->cn_cred);
if (flags & ISDOTDOT)
error = smbfs_smb_lookup(dnp->n_parent, NULL, 0, &fattr, &scred);
error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0,
&fattr, &scred);
else
error = smbfs_smb_lookup(dnp, name, nmlen, &fattr, &scred);