At mount/unmount time, add an exec hook to revoke all vnodes iff the

process is about to exec a sugid binary.

To speed up things, use hashing for vnode allocation, like other filesystems
do. This avoids walking the whole procfs node list in the revoke case too.
This commit is contained in:
fvdl 2000-01-25 21:52:04 +00:00
parent 701cc4b663
commit 15bb1bd145
3 changed files with 138 additions and 53 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: procfs.h,v 1.27 1999/09/02 23:33:45 thorpej Exp $ */
/* $NetBSD: procfs.h,v 1.28 2000/01/25 21:52:04 fvdl Exp $ */
/*
* Copyright (c) 1993 Jan-Simon Pendry
@ -63,7 +63,7 @@ typedef enum {
* control data for the proc file system.
*/
struct pfsnode {
struct pfsnode *pfs_next; /* next on list */
LIST_ENTRY(pfsnode) pfs_hash; /* hash chain */
struct vnode *pfs_vnode; /* vnode associated with this pfsnode */
pfstype pfs_type; /* type of procfs node */
pid_t pfs_pid; /* associated process */
@ -90,6 +90,14 @@ struct pfsnode {
((type) + 2) : \
((((pid)+1) << 4) + ((int) (type))))
struct procfsmount {
void *pmnt_exechook;
struct mount *pmnt_mp;
};
#define VFSTOPROC(mp) ((struct procfsmount *)(mp)->mnt_data)
#define PROCTOVFS(pp) ((pp)->pmnt_mp)
/*
* Convert between pfsnode vnode
*/
@ -127,6 +135,8 @@ int procfs_docmdline __P((struct proc *, struct proc *, struct pfsnode *,
struct uio *));
int procfs_checkioperm __P((struct proc *, struct proc *));
void procfs_revoke_vnodes __P((struct proc *, void *));
void procfs_hashinit __P((void));
/* functions to check whether or not files should be displayed */
int procfs_validfile __P((struct proc *));

View File

@ -1,4 +1,4 @@
/* $NetBSD: procfs_subr.c,v 1.28 1999/09/02 23:33:45 thorpej Exp $ */
/* $NetBSD: procfs_subr.c,v 1.29 2000/01/25 21:52:04 fvdl Exp $ */
/*
* Copyright (c) 1994 Christopher G. Demetriou. All rights reserved.
@ -51,8 +51,16 @@
#include <miscfs/procfs/procfs.h>
static struct pfsnode *pfshead;
static int pfsvplock;
void procfs_hashins __P((struct pfsnode *));
void procfs_hashrem __P((struct pfsnode *));
struct vnode *procfs_hashget __P((pid_t, pfstype, struct mount *));
LIST_HEAD(pfs_hashhead, pfsnode) *pfs_hashtbl;
u_long ihash; /* size of hash table - 1 */
#define PFSPIDHASH(pid) (&pfs_hashtbl[(pid) & ihash])
struct lock pfs_hashlock;
struct simplelock pfs_hash_slock;
#define ISSET(t, f) ((t) & (f))
@ -91,41 +99,23 @@ procfs_allocvp(mp, vpp, pid, pfs_type)
{
struct pfsnode *pfs;
struct vnode *vp;
struct pfsnode **pp;
int error;
loop:
for (pfs = pfshead; pfs != 0; pfs = pfs->pfs_next) {
vp = PFSTOV(pfs);
if (pfs->pfs_pid == pid &&
pfs->pfs_type == pfs_type &&
vp->v_mount == mp) {
if (vget(vp, LK_EXCLUSIVE))
goto loop;
*vpp = vp;
do {
if ((*vpp = procfs_hashget(pid, pfs_type, mp)) != NULL)
return (0);
}
}
} while (lockmgr(&pfs_hashlock, LK_EXCLUSIVE|LK_SLEEPFAIL, 0));
/*
* otherwise lock the vp list while we call getnewvnode
* since that can block.
*/
if (pfsvplock & PROCFS_LOCKED) {
pfsvplock |= PROCFS_WANT;
sleep((caddr_t) &pfsvplock, PINOD);
goto loop;
if ((error = getnewvnode(VT_PROCFS, mp, procfs_vnodeop_p, vpp)) != 0) {
*vpp = NULL;
lockmgr(&pfs_hashlock, LK_RELEASE, 0);
return (error);
}
pfsvplock |= PROCFS_LOCKED;
if ((error = getnewvnode(VT_PROCFS, mp, procfs_vnodeop_p, vpp)) != 0)
goto out;
vp = *vpp;
MALLOC(pfs, void *, sizeof(struct pfsnode), M_TEMP, M_WAITOK);
vp->v_data = pfs;
pfs->pfs_next = 0;
pfs->pfs_pid = (pid_t) pid;
pfs->pfs_type = pfs_type;
pfs->pfs_vnode = vp;
@ -176,20 +166,8 @@ loop:
panic("procfs_allocvp");
}
VOP_LOCK(vp, LK_EXCLUSIVE);
/* add to procfs vnode list */
for (pp = &pfshead; *pp; pp = &(*pp)->pfs_next)
continue;
*pp = pfs;
out:
pfsvplock &= ~PROCFS_LOCKED;
if (pfsvplock & PROCFS_WANT) {
pfsvplock &= ~PROCFS_WANT;
wakeup((caddr_t) &pfsvplock);
}
procfs_hashins(pfs);
lockmgr(&pfs_hashlock, LK_RELEASE, 0);
return (error);
}
@ -198,15 +176,9 @@ int
procfs_freevp(vp)
struct vnode *vp;
{
struct pfsnode **pfspp;
struct pfsnode *pfs = VTOPFS(vp);
for (pfspp = &pfshead; *pfspp != 0; pfspp = &(*pfspp)->pfs_next) {
if (*pfspp == pfs) {
*pfspp = pfs->pfs_next;
break;
}
}
procfs_hashrem(pfs);
FREE(vp->v_data, M_TEMP);
vp->v_data = 0;
@ -336,3 +308,91 @@ vfs_findname(nm, buf, buflen)
return (0);
}
/*
* Initialize pfsnode hash table.
*/
void
procfs_hashinit()
{
lockinit(&pfs_hashlock, PINOD, "pfs_hashlock", 0, 0);
pfs_hashtbl = hashinit(desiredvnodes / 4, M_UFSMNT, M_WAITOK, &ihash);
simple_lock_init(&pfs_hash_slock);
}
struct vnode *
procfs_hashget(pid, type, mp)
pid_t pid;
pfstype type;
struct mount *mp;
{
struct pfsnode *pp;
struct vnode *vp;
loop:
simple_lock(&pfs_hash_slock);
for (pp = PFSPIDHASH(pid)->lh_first; pp; pp = pp->pfs_hash.le_next) {
vp = PFSTOV(pp);
if (pid == pp->pfs_pid && pp->pfs_type == type &&
vp->v_mount == mp) {
simple_lock(&vp->v_interlock);
simple_unlock(&pfs_hash_slock);
if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK))
goto loop;
return (vp);
}
}
simple_unlock(&pfs_hash_slock);
return (NULL);
}
/*
* Insert the pfsnode into the hash table and lock it.
*/
void
procfs_hashins(pp)
struct pfsnode *pp;
{
struct pfs_hashhead *ppp;
/* lock the pfsnode, then put it on the appropriate hash list */
lockmgr(&pp->pfs_vnode->v_lock, LK_EXCLUSIVE, (struct simplelock *)0);
simple_lock(&pfs_hash_slock);
ppp = PFSPIDHASH(pp->pfs_pid);
LIST_INSERT_HEAD(ppp, pp, pfs_hash);
simple_unlock(&pfs_hash_slock);
}
/*
* Remove the pfsnode from the hash table.
*/
void
procfs_hashrem(pp)
struct pfsnode *pp;
{
simple_lock(&pfs_hash_slock);
LIST_REMOVE(pp, pfs_hash);
simple_unlock(&pfs_hash_slock);
}
void
procfs_revoke_vnodes(p, arg)
struct proc *p;
void *arg;
{
struct pfsnode *pfs, *pnext;
struct vnode *vp;
struct mount *mp = (struct mount *)arg;
if (!(p->p_flag & P_SUGID))
return;
for (pfs = PFSPIDHASH(p->p_pid)->lh_first; pfs; pfs = pnext) {
vp = PFSTOV(pfs);
pnext = pfs->pfs_hash.le_next;
if (vp->v_usecount > 0 && pfs->pfs_pid == p->p_pid &&
vp->v_mount == mp)
VOP_REVOKE(vp, REVOKEALL);
}
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: procfs_vfsops.c,v 1.31 1999/02/26 23:44:46 wrstuden Exp $ */
/* $NetBSD: procfs_vfsops.c,v 1.32 2000/01/25 21:52:04 fvdl Exp $ */
/*
* Copyright (c) 1993 Jan-Simon Pendry
@ -57,6 +57,7 @@
#include <sys/mount.h>
#include <sys/signalvar.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <miscfs/procfs/procfs.h>
#include <vm/vm.h> /* for PAGE_SIZE */
@ -91,6 +92,7 @@ procfs_mount(mp, path, data, ndp, p)
struct proc *p;
{
size_t size;
struct procfsmount *pmnt;
if (UIO_MX & (UIO_MX-1)) {
log(LOG_ERR, "procfs: invalid directory entry size");
@ -101,13 +103,20 @@ procfs_mount(mp, path, data, ndp, p)
return (EOPNOTSUPP);
mp->mnt_flag |= MNT_LOCAL;
mp->mnt_data = 0;
pmnt = (struct procfsmount *) malloc(sizeof(struct procfsmount),
M_UFSMNT, M_WAITOK); /* XXX need new malloc type */
mp->mnt_data = (qaddr_t)pmnt;
vfs_getnewfsid(mp, MOUNT_PROCFS);
(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN, &size);
memset(mp->mnt_stat.f_mntonname + size, 0, MNAMELEN - size);
memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
memcpy(mp->mnt_stat.f_mntfromname, "procfs", sizeof("procfs"));
pmnt->pmnt_exechook = exechook_establish(procfs_revoke_vnodes, mp);
pmnt->pmnt_mp = mp;
return (0);
}
@ -129,6 +138,11 @@ procfs_unmount(mp, mntflags, p)
if ((error = vflush(mp, 0, flags)) != 0)
return (error);
exechook_disestablish(VFSTOPROC(mp)->pmnt_exechook);
free(mp->mnt_data, M_UFSMNT);
mp->mnt_data = 0;
return (0);
}
@ -255,6 +269,7 @@ procfs_vptofh(vp, fhp)
void
procfs_init()
{
procfs_hashinit();
}
int