Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/netbsd-2-0]: src/sys/nfs Pull up revision 1.135 (requested by yamt in ti...
details: https://anonhg.NetBSD.org/src/rev/3ef9796fb052
branches: netbsd-2-0
changeset: 562664:3ef9796fb052
user: he <he%NetBSD.org@localhost>
date: Sat Sep 18 19:21:45 2004 +0000
description:
Pull up revision 1.135 (requested by yamt in ticket #858):
Fix access-after-free bugs in dircache code by reference
counting nfsdircache. Fixes PR#26864.
diffstat:
sys/nfs/nfs_subs.c | 189 ++++++++++++++++++++++++++++++++++++++++------------
1 files changed, 145 insertions(+), 44 deletions(-)
diffs (truncated from 326 to 300 lines):
diff -r 0383a7d9900a -r 3ef9796fb052 sys/nfs/nfs_subs.c
--- a/sys/nfs/nfs_subs.c Sat Sep 18 19:21:23 2004 +0000
+++ b/sys/nfs/nfs_subs.c Sat Sep 18 19:21:45 2004 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: nfs_subs.c,v 1.132.2.1 2004/06/21 10:15:57 tron Exp $ */
+/* $NetBSD: nfs_subs.c,v 1.132.2.2 2004/09/18 19:21:45 he Exp $ */
/*
* Copyright (c) 1989, 1993
@@ -70,7 +70,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nfs_subs.c,v 1.132.2.1 2004/06/21 10:15:57 tron Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nfs_subs.c,v 1.132.2.2 2004/09/18 19:21:45 he Exp $");
#include "fs_nfs.h"
#include "opt_nfs.h"
@@ -1214,19 +1214,32 @@
return sum;
}
+#define _NFSDC_MTX(np) (&NFSTOV(np)->v_interlock)
+#define NFSDC_LOCK(np) simple_lock(_NFSDC_MTX(np))
+#define NFSDC_UNLOCK(np) simple_unlock(_NFSDC_MTX(np))
+#define NFSDC_ASSERT_LOCKED(np) LOCK_ASSERT(simple_lock_held(_NFSDC_MTX(np)))
+
void
nfs_initdircache(vp)
struct vnode *vp;
{
struct nfsnode *np = VTONFS(vp);
+ struct nfsdirhashhead *dircache;
- KASSERT(np->n_dircache == NULL);
+ dircache = hashinit(NFS_DIRHASHSIZ, HASH_LIST, M_NFSDIROFF,
+ M_WAITOK, &nfsdirhashmask);
- np->n_dircachesize = 0;
- np->n_dblkno = 1;
- np->n_dircache = hashinit(NFS_DIRHASHSIZ, HASH_LIST, M_NFSDIROFF,
- M_WAITOK, &nfsdirhashmask);
- TAILQ_INIT(&np->n_dirchain);
+ NFSDC_LOCK(np);
+ if (np->n_dircache == NULL) {
+ np->n_dircachesize = 0;
+ np->n_dblkno = 1;
+ np->n_dircache = dircache;
+ dircache = NULL;
+ TAILQ_INIT(&np->n_dirchain);
+ }
+ NFSDC_UNLOCK(np);
+ if (dircache)
+ hashdone(dircache, M_NFSDIROFF);
}
void
@@ -1234,16 +1247,83 @@
struct vnode *vp;
{
struct nfsnode *np = VTONFS(vp);
+ unsigned *dirgens;
KASSERT(VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_XLATECOOKIE);
- KASSERT(np->n_dirgens == NULL);
- MALLOC(np->n_dirgens, unsigned *,
- NFS_DIRHASHSIZ * sizeof (unsigned), M_NFSDIROFF, M_WAITOK);
- memset((caddr_t)np->n_dirgens, 0, NFS_DIRHASHSIZ * sizeof (unsigned));
+ dirgens = malloc(NFS_DIRHASHSIZ * sizeof (unsigned), M_NFSDIROFF,
+ M_WAITOK|M_ZERO);
+ NFSDC_LOCK(np);
+ if (np->n_dirgens == NULL) {
+ np->n_dirgens = dirgens;
+ dirgens = NULL;
+ }
+ NFSDC_UNLOCK(np);
+ if (dirgens)
+ free(dirgens, M_NFSDIROFF);
}
-static struct nfsdircache dzero = {0, 0, {0, 0}, {0, 0}, 0, 0, 0};
+static const struct nfsdircache dzero;
+
+static void nfs_unlinkdircache __P((struct nfsnode *np, struct nfsdircache *));
+static void nfs_putdircache_unlocked __P((struct nfsnode *,
+ struct nfsdircache *));
+
+static void
+nfs_unlinkdircache(np, ndp)
+ struct nfsnode *np;
+ struct nfsdircache *ndp;
+{
+
+ NFSDC_ASSERT_LOCKED(np);
+ KASSERT(ndp != &dzero);
+
+ if (LIST_NEXT(ndp, dc_hash) == (void *)-1)
+ return;
+
+ TAILQ_REMOVE(&np->n_dirchain, ndp, dc_chain);
+ LIST_REMOVE(ndp, dc_hash);
+ LIST_NEXT(ndp, dc_hash) = (void *)-1; /* mark as unlinked */
+
+ nfs_putdircache_unlocked(np, ndp);
+}
+
+void
+nfs_putdircache(np, ndp)
+ struct nfsnode *np;
+ struct nfsdircache *ndp;
+{
+ int ref;
+
+ if (ndp == &dzero)
+ return;
+
+ KASSERT(ndp->dc_refcnt > 0);
+ NFSDC_LOCK(np);
+ ref = --ndp->dc_refcnt;
+ NFSDC_UNLOCK(np);
+
+ if (ref == 0)
+ free(ndp, M_NFSDIROFF);
+}
+
+static void
+nfs_putdircache_unlocked(np, ndp)
+ struct nfsnode *np;
+ struct nfsdircache *ndp;
+{
+ int ref;
+
+ NFSDC_ASSERT_LOCKED(np);
+
+ if (ndp == &dzero)
+ return;
+
+ KASSERT(ndp->dc_refcnt > 0);
+ ref = --ndp->dc_refcnt;
+ if (ref == 0)
+ free(ndp, M_NFSDIROFF);
+}
struct nfsdircache *
nfs_searchdircache(vp, off, do32, hashent)
@@ -1261,7 +1341,8 @@
* Zero is always a valid cookie.
*/
if (off == 0)
- return &dzero;
+ /* LINTED const cast away */
+ return (struct nfsdircache *)&dzero;
if (!np->n_dircache)
return NULL;
@@ -1281,6 +1362,8 @@
if (hashent)
*hashent = (int)(ndhp - np->n_dircache);
+
+ NFSDC_LOCK(np);
if (do32) {
LIST_FOREACH(ndp, ndhp, dc_hash) {
if (ndp->dc_cookie32 == (u_int32_t)off) {
@@ -1289,10 +1372,11 @@
* start of a new block fetched from
* the server.
*/
- if (ndp->dc_blkno == -1) {
+ if (ndp->dc_flags & NFSDC_INVALID) {
ndp->dc_blkcookie = ndp->dc_cookie;
ndp->dc_blkno = np->n_dblkno++;
ndp->dc_entry = 0;
+ ndp->dc_flags &= ~NFSDC_INVALID;
}
break;
}
@@ -1303,6 +1387,9 @@
break;
}
}
+ if (ndp != NULL)
+ ndp->dc_refcnt++;
+ NFSDC_UNLOCK(np);
return ndp;
}
@@ -1316,10 +1403,20 @@
{
struct nfsnode *np = VTONFS(vp);
struct nfsdirhashhead *ndhp;
- struct nfsdircache *ndp = NULL, *first;
+ struct nfsdircache *ndp = NULL;
+ struct nfsdircache *newndp = NULL;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int hashent, gen, overwrite;
+ /*
+ * XXX refuse entries for offset 0. amd(8) erroneously sets
+ * cookie 0 for the '.' entry, making this necessary. This
+ * isn't so bad, as 0 is a special case anyway.
+ */
+ if (off == 0)
+ /* LINTED const cast away */
+ return (struct nfsdircache *)&dzero;
+
if (!np->n_dircache)
/*
* XXX would like to do this in nfs_nget but vtype
@@ -1330,34 +1427,34 @@
if ((nmp->nm_flag & NFSMNT_XLATECOOKIE) && !np->n_dirgens)
nfs_initdirxlatecookie(vp);
- /*
- * XXX refuse entries for offset 0. amd(8) erroneously sets
- * cookie 0 for the '.' entry, making this necessary. This
- * isn't so bad, as 0 is a special case anyway.
- */
- if (off == 0)
- return &dzero;
-
+retry:
ndp = nfs_searchdircache(vp, off, 0, &hashent);
- if (ndp && ndp->dc_blkno != -1) {
+ NFSDC_LOCK(np);
+ if (ndp && (ndp->dc_flags & NFSDC_INVALID) == 0) {
/*
* Overwriting an old entry. Check if it's the same.
* If so, just return. If not, remove the old entry.
*/
if (ndp->dc_blkcookie == blkoff && ndp->dc_entry == en)
- return ndp;
- TAILQ_REMOVE(&np->n_dirchain, ndp, dc_chain);
- LIST_REMOVE(ndp, dc_hash);
- FREE(ndp, M_NFSDIROFF);
- ndp = 0;
+ goto done;
+ nfs_unlinkdircache(np, ndp);
+ nfs_putdircache_unlocked(np, ndp);
+ ndp = NULL;
}
ndhp = &np->n_dircache[hashent];
if (!ndp) {
- MALLOC(ndp, struct nfsdircache *, sizeof (*ndp), M_NFSDIROFF,
- M_WAITOK);
+ if (newndp == NULL) {
+ NFSDC_UNLOCK(np);
+ newndp = malloc(sizeof(*ndp), M_NFSDIROFF, M_WAITOK);
+ newndp->dc_refcnt = 1;
+ LIST_NEXT(newndp, dc_hash) = (void *)-1;
+ goto retry;
+ }
+ ndp = newndp;
+ newndp = NULL;
overwrite = 0;
if (nmp->nm_flag & NFSMNT_XLATECOOKIE) {
/*
@@ -1388,7 +1485,7 @@
ndp->dc_entry = en;
if (overwrite)
- return ndp;
+ goto done;
/*
* If the maximum directory cookie cache size has been reached
@@ -1398,15 +1495,19 @@
* loss.
*/
if (np->n_dircachesize == NFS_MAXDIRCACHE) {
- first = TAILQ_FIRST(&np->n_dirchain);
- TAILQ_REMOVE(&np->n_dirchain, first, dc_chain);
- LIST_REMOVE(first, dc_hash);
- FREE(first, M_NFSDIROFF);
+ nfs_unlinkdircache(np, TAILQ_FIRST(&np->n_dirchain));
} else
np->n_dircachesize++;
+ KASSERT(ndp->dc_refcnt == 1);
LIST_INSERT_HEAD(ndhp, ndp, dc_hash);
TAILQ_INSERT_TAIL(&np->n_dirchain, ndp, dc_chain);
+ ndp->dc_refcnt++;
+done:
+ KASSERT(ndp->dc_refcnt > 0);
+ NFSDC_UNLOCK(np);
+ if (newndp)
+ nfs_putdircache(np, newndp);
return ndp;
}
@@ -1427,11 +1528,11 @@
if (!np->n_dircache)
return;
+ NFSDC_LOCK(np);
if (!(nmp->nm_flag & NFSMNT_XLATECOOKIE) || forcefree) {
Home |
Main Index |
Thread Index |
Old Index