Re: Enterable namespaces: /proc/pid/$ns/srv

"Frank D. Engel, Jr." <[email protected]> Sat, 13 Dec 2025 21:58:15 -0500
Newsgroups gmane.os.9front.general,gmane.os.plan9.general
Message-ID <[email protected]>
Doesn't that create a security hole by making it possible to access to=20
private ramdisks and factotum instances and the like which were=20
previously not accessible to the process?


On 12/13/25 19:54, ori-C8/[email protected] wrote:
> This patch makes it possible to enter a namespace through
> /proc/$pid/ns, like so:
>
> 	auth/newns -n /proc/$pid/ns $cmd
>
> or through procesing the namespace file any other way
> that they want.
>
> with the hope it'll be useful for things like fixing up
> tab completion in rio, making the plumber responsive to
> the local namespace that we are plumbing from, and other
> things that we haven't figured out yet.
>
> It works by making the chans for a proc's mounts available
> via /proc/$pid/srv/$id, and changing /proc/$pid/ns to use
> the channel name rather than files other procs can't access,
> such as '#|'.
>
> Hopefully folks will be able to experiment a bit with this,
> and figure out some good uses for it.
>
>
> diff 4341e26ac5d5cb86af2e1d88dd85365e7feb3a59 uncommitted
> --- a/sys/man/3/proc
> +++ b/sys/man/3/proc
> @@ -22,6 +22,7 @@
>   .BI /proc/ n /profile
>   .BI /proc/ n /regs
>   .BI /proc/ n /segment
> +.BI /proc/ n /srv/ chan
>   .BI /proc/ n /status
>   .BI /proc/ n /text
>   .BI /proc/ n /wait
> @@ -127,6 +128,15 @@
>   for read-only, if any;
>   starting virtual address, in hexadecimal;
>   ending virtual address, and reference count.
> +.PP
> +The
> +.B srv
> +directory provides a directory holding already-open channels
> +to services in the style of
> +.IR srv (3).
> +This allows a process with appropriate permissions to execute the
> +.I ns
> +file and accurately reproduce the namespace of the process that is runni=
ng.
>   .PP
>   The read-only
>   .B status
> --- a/sys/src/9/port/devproc.c
> +++ b/sys/src/9/port/devproc.c
> @@ -28,6 +28,8 @@
>   	Qnoteid,
>   	Qnotepg,
>   	Qns,
> +	Qsrv,
> +	Qchan,
>   	Qppid,
>   	Qproc,
>   	Qregs,
> @@ -100,6 +102,7 @@
>   	"noteid",	{Qnoteid},	0,			0664,
>   	"notepg",	{Qnotepg},	0,			0000,
>   	"ns",		{Qns},		0,			0444,
> +	"srv",		{Qsrv,0,QTDIR},	0,			0555,
>   	"ppid",		{Qppid},	0,			0444,
>   	"proc",		{Qproc},	0,			0400,
>   	"regs",		{Qregs},	sizeof(Ureg),		0000,
> @@ -154,8 +157,8 @@
>    *	     in vers,
>    *	32 bits of pid, for consistency checking
>    */
> -#define	QSHIFT	5	/* location in qid of proc slot # */
> -
> +#define	QSHIFT		5	/* location in qid of proc slot # */
> +#define CSHIFT		(26+5)	/* location in qid of chan # */
>   #define	QID(q)		((((ulong)(q).path)&0x0000001F)>>0)
>   #define	SLOTMAX		0x4000000
>   #define	SLOT(q)		(((((ulong)(q).path)>>QSHIFT)&(SLOTMAX-1))-1)
> @@ -175,6 +178,39 @@
>  =20
>   static int lenwatchpt(Proc *);
>  =20
> +static Chan*
> +mntchan(Proc *p, Chan *c)
> +{
> +	char *s, *t, buf[32];
> +	Chan *nc, *mc;
> +	Pgrp *pg;
> +	Mount *f;
> +
> +	s =3D c->path->s;
> +	if((t =3D strrchr(s, '/')) !=3D nil)
> +		s =3D t+1;
> +
> +	pg =3D p->pgrp;
> +	if(pg =3D=3D nil)
> +		error(Eprocdied);
> +
> +	nc =3D nil;
> +	rlock(&pg->ns);
> +	for(f =3D pg->mntorder; f !=3D nil; f =3D f->order){
> +		if(strcmp(f->to->path->s, "#M") !=3D 0)
> +			continue;
> +		mc =3D f->to->mchan;
> +		snprint(buf, sizeof(buf), "%ld.%x.%llx", mc->dev, mc->type, mc->qid.pa=
th);
> +		if(strcmp(s, buf) =3D=3D 0){
> +			nc =3D f->to->mchan;
> +			incref(nc);
> +			break;
> +		}
> +	}
> +	runlock(&pg->ns);
> +	return nc;
> +}
> +
>   static int
>   procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
>   {
> @@ -182,7 +218,13 @@
>   	Proc *p;
>   	char *ename;
>   	Segment *q;
> -	ulong pid, path, perm, len;
> +	Mount *f;
> +	Chan *mc;
> +	Pgrp *pg;
> +	ulong pid, perm, len;
> +	uvlong path;
> +	char *e, *t;
> +	int i;
>  =20
>   	if(s =3D=3D DEVDOTDOT){
>   		mkqid(&qid, Qdir, 0, QTDIR);
> @@ -226,9 +268,46 @@
>   		devdir(c, qid, up->genbuf, 0, p->user, 0555, dp);
>   		return 1;
>   	}
> +	if(QID(c->qid) =3D=3D Qsrv){
> +		i =3D 0;
> +		p =3D proctab(SLOT(c->qid));
> +		pg =3D p->pgrp;
> +		if(pg =3D=3D nil || p->dot =3D=3D nil || p->pid !=3D PID(c->qid))
> +			error(Eprocdied);
> +		rlock(&pg->ns);
> +		for(f =3D pg->mntorder; f !=3D nil; f =3D f->order){
> +			if(strcmp(f->to->path->s, "#M") !=3D 0)
> +				continue;
> +			if(i++ >=3D s)
> +				break;
> +		}
> +		if(f =3D=3D nil){
> +			runlock(&pg->ns);
> +			return -1;
> +		}
> +		mc =3D f->to->mchan;
> +		path =3D ((uvlong)i<<CSHIFT) | c->qid.path&~((1<<QSHIFT)-1);
> +		snprint(up->genbuf, sizeof(up->genbuf), "%ld.%x.%llx", mc->dev, mc->ty=
pe, mc->qid.path);
> +		mkqid(&qid, path | Qchan, p->pid, QTFILE);
> +		devdir(c, qid, up->genbuf, 0, p->user, 0400, dp);
> +		runlock(&pg->ns);
> +		return 1;
> +	}
> +	if(QID(c->qid) =3D=3D Qchan){
> +		p =3D proctab(SLOT(c->qid));
> +		if(p->dot =3D=3D nil || p->pid !=3D PID(c->qid))
> +			error(Eprocdied);
> +		e =3D c->path->s;
> +		if((t =3D strrchr(e, '/')) !=3D nil)
> +			e =3D t+1;
> +		snprint(up->genbuf, sizeof(up->genbuf), "%s", e);
> +		mkqid(&qid, c->qid.path, p->pid, QTFILE);
> +		devdir(c, qid, up->genbuf, 0, eve, 0400, dp);
> +		return 1;
> +	}
>   	if(c->qid.path =3D=3D Qtrace){
>   		strcpy(up->genbuf, "trace");
> -		mkqid(&qid, Qtrace, -1, QTFILE);
> +		mkqid(&qid, c->qid.path, -1, QTFILE);
>   		devdir(c, qid, up->genbuf, 0, eve, 0400, dp);
>   		return 1;
>   	}
> @@ -265,7 +344,7 @@
>   		break;
>   	}
>  =20
> -	mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
> +	mkqid(&qid, path|tab->qid.path, c->qid.vers, tab->qid.type);
>   	devdir(c, qid, tab->name, len, p->user, perm, dp);
>   	return 1;
>   }
> @@ -398,6 +477,7 @@
>   		c->offset =3D 0;
>   		return c;
>   	}
> +
>   	=09
>   	p =3D proctab(SLOT(c->qid));
>   	eqlock(&p->debug);
> @@ -436,7 +516,13 @@
>   		if(omode !=3D OREAD)
>   			error(Eperm);
>   		break;
> -
> +	case Qchan:
> +		tc =3D mntchan(p, c);
> +		if(tc =3D=3D nil)
> +			error(Eshutdown);
> +		devpermcheck(p->user, c->mode, omode);
> +		nonone(p);
> +		goto Found;
>   	case Qctl:
>   	case Qargs:
>   	case Qwait:
> @@ -488,8 +574,8 @@
>   			clearwatchpt(p);
>   		break;
>   	}
> -=09
>   	poperror();
> +Found:
>   	qunlock(&p->debug);
>   	poperror();
>  =20
> @@ -641,10 +727,11 @@
>   static int
>   readns1(Chan *c, Proc *p, char *buf, int nbuf)
>   {
> +	char flag[10], srv[32];
>   	Pgrp *pg;
>   	Mount *f;
> -	char flag[10], *srv;
> -	int i;
> +	Chan *mc;
> +	int i, n;
>  =20
>   	pg =3D p->pgrp;
>   	if(pg =3D=3D nil || p->dot =3D=3D nil || p->pid !=3D PID(c->qid))
> @@ -656,32 +743,33 @@
>   	rlock(&pg->ns);
>  =20
>   	i =3D 0;
> -	for(f =3D pg->mntorder; f !=3D nil; f =3D f->order) {
> +	for(f =3D pg->mntorder; f !=3D nil; f =3D f->order)
>   		if(i++ >=3D c->nrock)
>   			break;
> -	}
>  =20
>   	if(f =3D=3D nil) {
>   		c->nrock =3D -1;
> -		i =3D snprint(buf, nbuf, "cd %q\n", p->dot->path->s);
> +		n =3D snprint(buf, nbuf, "cd %q\n", p->dot->path->s);
>   	} else {
>   		c->nrock =3D i;
>   		int2flag(f->mflag, flag);
>   		if(strcmp(f->to->path->s, "#M") =3D=3D 0){
> -			srv =3D f->to->mchan->srvname;
> -			if(srv =3D=3D nil)
> -				srv =3D f->to->mchan->path->s;
> -			i =3D snprint(buf, nbuf, *f->spec?
> -				"mount %s %q %q %q\n": "mount %s %q %q\n", flag,
> -				srv, f->umh->from->path->s, f->spec);
> +			mc =3D f->to->mchan;
> +			snprint(srv, sizeof(srv), "%ld.%x.%llx", mc->dev, mc->type, mc->qid.p=
ath);
> +			if(f->spec[0] !=3D 0)
> +				n =3D snprint(buf, nbuf, "mount %s /proc/%lud/srv/%q %q %q\n",
> +					flag, p->pid, srv, f->umh->from->path->s, f->spec);
> +			else
> +				n =3D snprint(buf, nbuf, "mount %s /proc/%lud/srv/%q %q\n",
> +					flag, p->pid, srv, f->umh->from->path->s);
>   		}else{
> -			i =3D snprint(buf, nbuf, "bind %s %q %q\n", flag,
> +			n =3D snprint(buf, nbuf, "bind %s %q %q\n", flag,
>   				f->to->path->s, f->umh->from->path->s);
>   		}
>   	}
>   	runlock(&pg->ns);
>  =20
> -	return i;
> +	return n;
>   }
>  =20
>   int
>