NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: kern/41154: possible races in NFS server code ?
On Sun, Apr 05, 2009 at 09:05:00PM +0000, Manuel Bouyer wrote:
> >Description:
> While working on the NFS server code I noticed that several
> places use members of struct nfssvc_sock in a way that is not
> obviously thread-safe.
> There appears to be no locking to prevent a nfssvc_sock that has been
> checked as SLP_VALID to become invalid while in use.
> Some places recheck SLP_VALID where appropriate but some don't.
> Especially I suspect there's a possible use after free of
> ns_nam in nfssvc_nfsd(), as nothing seems to prevent nfsrv_zapsock()
> from freeing it while nd_nam points to it.
For this specific issue, I moved the mfree of nd_nam to nfsrv_slpderef().
See attached patch
> More generally the tail
> of nfssvc_nfsd() doens't look safe.
>
> If concurent execution of nfssvc_addsock() and nfsrv_zapsock()
> possible ? If so nfssvc_addsock() would need to take the ns_lock,
> and revisit the way SLP_VALID is handled in these 2 functions.
After more though about this, it doens't seem to be possible.
>
> Last I suspect a race between sys_nfssvc() and nfsrv_zapsock()
> could cause a corruption of the UID hash lists. More generally
> what prevents concurent updates of the ns_uidlruhead ?
This question remains.
I found another possible race involving nfsrv_slpderef() which could lead
to a use after free of the slp. This involves one thread
calling nfsrv_slpderef(), another one grabbing slp from the
nfssvc_sockpending list and zapping it.
My fix for this is to not release the nfsd_lock between decrementing the
refcount and testing SLP_VALID.
there is also a place still using splsoftclock(), I gues it has been
forgotten. Without tacking nfsd_lock here we could dereference a NULL
pointer if we're being unlucky.
--
Manuel Bouyer <bouyer%antioche.eu.org@localhost>
NetBSD: 26 ans d'experience feront toujours la difference
--
Index: nfs_syscalls.c
===================================================================
RCS file: /cvsroot/src/sys/nfs/nfs_syscalls.c,v
retrieving revision 1.140
diff -u -r1.140 nfs_syscalls.c
--- nfs_syscalls.c 9 Oct 2008 14:38:21 -0000 1.140
+++ nfs_syscalls.c 8 Apr 2009 18:46:50 -0000
@@ -511,7 +511,6 @@
u_quad_t cur_usec;
int error = 0, cacherep, siz, sotype, writes_todo;
struct proc *p = l->l_proc;
- int s;
bool doreinit;
#ifndef nolint
@@ -761,14 +761,14 @@
getmicrotime(&tv);
cur_usec = (u_quad_t)tv.tv_sec * 1000000 +
(u_quad_t)tv.tv_usec;
- s = splsoftclock();
+ mutex_enter(&nfsd_lock);
if (LIST_FIRST(&slp->ns_tq) &&
LIST_FIRST(&slp->ns_tq)->nd_time <= cur_usec) {
cacherep = RC_DOIT;
writes_todo = 1;
} else
writes_todo = 0;
- splx(s);
+ mutex_exit(&nfsd_lock);
} while (writes_todo);
if (nfsrv_dorec(slp, nfsd, &nd, &dummy)) {
nfsd->nfsd_slp = NULL;
@@ -828,8 +828,6 @@
soshutdown(so, SHUT_RDWR);
sounlock(so);
- if (slp->ns_nam)
- m_free(slp->ns_nam);
m_freem(slp->ns_raw);
m = slp->ns_rec;
while (m != NULL) {
@@ -839,8 +837,9 @@
m_freem(m);
m = n;
}
+ /* XXX what about freeing ns_frag ? */
for (nuidp = TAILQ_FIRST(&slp->ns_uidlruhead); nuidp != 0;
nuidp = nnuidp) {
nnuidp = TAILQ_NEXT(nuidp, nu_lru);
LIST_REMOVE(nuidp, nu_hash);
TAILQ_REMOVE(&slp->ns_uidlruhead, nuidp, nu_lru);
@@ -871,11 +871,9 @@
mutex_enter(&nfsd_lock);
KASSERT(slp->ns_sref > 0);
ref = --slp->ns_sref;
- mutex_exit(&nfsd_lock);
if (ref == 0 && (slp->ns_flags & SLP_VALID) == 0) {
file_t *fp;
- mutex_enter(&nfsd_lock);
KASSERT((slp->ns_gflags & SLP_G_DOREC) == 0);
TAILQ_REMOVE(&nfssvc_sockhead, slp, ns_chain);
mutex_exit(&nfsd_lock);
@@ -889,9 +887,12 @@
closef(fp);
slp->ns_so = NULL;
}
-
+
+ if (slp->ns_nam)
+ m_free(slp->ns_nam);
nfsrv_sockfree(slp);
- }
+ } else
+ mutex_exit(&nfsd_lock);
}
/*
Home |
Main Index |
Thread Index |
Old Index