In mount_checkdirs you can find a loop:
mutex_enter(proc_lock);
PROCLIST_FOREACH(p, &allproc) {
if ((cwdi = p->p_cwdi) == NULL)
continue;
if (cwdi->cwdi_cdir != olddp &&
cwdi->cwdi_rdir != olddp)
continue;
retry = true;
rele1 = NULL;
rele2 = NULL;
atomic_inc_uint(&cwdi->cwdi_refcnt);
mutex_exit(proc_lock);
rw_enter(&cwdi->cwdi_lock, RW_WRITER);
The problem is that p_cwdi IS NOT protected by proc_lock. The exit path
uses reflock to destroy the object.
Since this function is not performance critical, my suggested fix is to
uncondionally take the p_lock and ref cwdi only if the process is alive
and constructed.
Alternatively, cwdi freeing can be split into 2 parts where actual freeing
of the cwdi object itself is postponed until after proc_lock lock/unlock
dance.
Pseudo-code:
struct cwdinfo *
cwddestroy(struct cwdinfo *cwdi)
{
if (atomic_dec_uint_nv(&cwdi->cwdi_refcnt) > 0)
return NULL;
vrele(cwdi->cwdi_cdir);
if (cwdi->cwdi_rdir)
vrele(cwdi->cwdi_rdir);
if (cwdi->cwdi_edir)
vrele(cwdi->cwdi_edir);
return cwdi;
}
void
cwdfree(struct cwdinfo *cwdi)
{
pool_cache_put(cwdi_cache, cwdi);
}
Then the exit path would:
cwdi = cwddestroy(p->p_cwdi);
p->p_cwdi = NULL;
....
mutex_enter(proc_lock);
....
mutex_exit(proc_lock);
cwdfree(cwdi);
================
mount_checkdirs would then atomic_inc_not_zero (or however you have this
named).
With the above, if cwdi was spotted, it is guaranteeed to not get freed
until after proc_lock is dropped. A successfull non-zero -> non-zero + 1
refcount bump guarantees it wont get freed and the content will remain
valid.
--