shithub: riscv

Download patch

ref: 0b33b3b8adf95bcf6cf0764fe425169ee0b8be0e
parent: b2b2d2cb4c5cb3153760084a55584817a2c58a24
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Dec 13 11:04:09 EST 2020

kernel: implement per file descriptor OCEXEC flag, reject ORCLOSE when opening /fd, /srv and /shr

The OCEXEC flag used to be maintained per channel,
making it shared between all the file desciptors.

This has a unexpected side effects with regard to
channel passing drivers such as devdup (/fd),
devsrv (/srv) and devshr (/shr).

For example, opening a /srv file with OCEXEC
makes it impossible to be remounted by exportfs
as it internally does a exec() to mount and
re-export it. There is no way to reset the flag.

This change makes the OCEXEC flag per file descriptor,
so a open with the OCEXEC flag only affects the fd
group of the calling process, and not the channel
itself.

On rfork(RFFDG), the per file descriptor flags get
copied.

On dup(), the per file descriptor flags are reset.

The second modification is that /fd, /srv and /shr
should reject the ORCLOSE flag, as the files that
are returned have already been opend.

--- a/sys/man/2/dup
+++ b/sys/man/2/dup
@@ -32,6 +32,19 @@
 .I newfd
 be no greater than 20 more than the highest file descriptor ever used by
 the program.
+.PP
+.I Dup
+does not copy the per file descriptor
+.B OCEXEC
+flag,
+meaning that
+.I newfd
+will not be closed on
+.IR exec(2)
+syscall,
+when
+.I oldfd
+had been previously opend with it.
 .SH SOURCE
 .B /sys/src/libc/9syscall
 .SH SEE ALSO
--- a/sys/src/9/port/auth.c
+++ b/sys/src/9/port/auth.c
@@ -97,13 +97,12 @@
 		nexterror();
 	}
 
-	fd = newfd(ac);
+	/* always mark it close on exec */
+	fd = newfd(ac, OCEXEC);
 	if(fd < 0)
 		error(Enofd);
 	poperror();	/* ac */
 
-	/* always mark it close on exec */
-	ac->flag |= CCEXEC;
 	return (uintptr)fd;
 }
 
--- a/sys/src/9/port/chan.c
+++ b/sys/src/9/port/chan.c
@@ -1468,9 +1468,6 @@
 			saveregisters();
 
 			c = devtab[c->type]->open(c, omode&~OCEXEC);
-
-			if(omode & OCEXEC)
-				c->flag |= CCEXEC;
 			if(omode & ORCLOSE)
 				c->flag |= CRCLOSE;
 			break;
@@ -1571,11 +1568,9 @@
 			incref(cnew->path);
 
 			cnew = devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm);
-			poperror();
-			if(omode & OCEXEC)
-				cnew->flag |= CCEXEC;
 			if(omode & ORCLOSE)
 				cnew->flag |= CRCLOSE;
+			poperror();
 			putmhead(m);
 			cclose(c);
 			c = cnew;
--- a/sys/src/9/port/devdup.c
+++ b/sys/src/9/port/devdup.c
@@ -63,6 +63,8 @@
 	Chan *f;
 	int fd, twicefd;
 
+	if(omode & ORCLOSE)
+		error(Eperm);
 	if(c->qid.type & QTDIR){
 		if(omode != 0)
 			error(Eisdir);
--- a/sys/src/9/port/devshr.c
+++ b/sys/src/9/port/devshr.c
@@ -396,6 +396,8 @@
 	case Qcmpt:
 		if(omode&OTRUNC)
 			error(Eexist);
+		if(omode&ORCLOSE)
+			error(Eperm);
 		shr = sch->shr;
 		mpt = sch->mpt;
 		devpermcheck(mpt->owner, mpt->perm, mode);
--- a/sys/src/9/port/devsrv.c
+++ b/sys/src/9/port/devsrv.c
@@ -135,6 +135,8 @@
 
 	if(omode&OTRUNC)
 		error(Eexist);
+	if(omode&ORCLOSE)
+		error(Eperm);
 	if(openmode(omode)!=sp->chan->mode && sp->chan->mode!=ORDWR)
 		error(Eperm);
 	devpermcheck(sp->owner, sp->perm, omode);
@@ -338,8 +340,6 @@
 		cclose(c1);
 		nexterror();
 	}
-	if(c1->flag & (CCEXEC|CRCLOSE))
-		error("posted fd has remove-on-close or close-on-exec");
 	if(c1->qid.type & QTAUTH)
 		error("cannot post auth file in srv");
 	sp = srvlookup(nil, c->qid.path);
--- a/sys/src/9/port/lib.h
+++ b/sys/src/9/port/lib.h
@@ -176,7 +176,7 @@
 #define	ORDWR	2	/* read and write */
 #define	OEXEC	3	/* execute, == read but check execute permission */
 #define	OTRUNC	16	/* or'ed in (except for exec), truncate file first */
-#define	OCEXEC	32	/* or'ed in, close on exec */
+#define	OCEXEC	32	/* or'ed in (per file descriptor), close on exec */
 #define	ORCLOSE	64	/* or'ed in, remove on close */
 #define OEXCL   0x1000	/* or'ed in, exclusive create */
 
--- a/sys/src/9/port/pgrp.c
+++ b/sys/src/9/port/pgrp.c
@@ -140,7 +140,8 @@
 
 	new = smalloc(sizeof(Fgrp));
 	if(f == nil){
-		new->fd = smalloc(DELTAFD*sizeof(Chan*));
+		new->flag = smalloc(DELTAFD*sizeof(new->flag[0]));
+		new->fd = smalloc(DELTAFD*sizeof(new->fd[0]));
 		new->nfd = DELTAFD;
 		new->ref = 1;
 		return new;
@@ -152,12 +153,19 @@
 	i = new->nfd%DELTAFD;
 	if(i != 0)
 		new->nfd += DELTAFD - i;
-	new->fd = malloc(new->nfd*sizeof(Chan*));
+	new->fd = malloc(new->nfd*sizeof(new->fd[0]));
 	if(new->fd == nil){
 		unlock(f);
 		free(new);
 		error("no memory for fgrp");
 	}
+	new->flag = malloc(new->nfd*sizeof(new->flag[0]));
+	if(new->flag == nil){
+		unlock(f);
+		free(new->fd);
+		free(new);
+		error("no memory for fgrp");
+	}
 	new->ref = 1;
 
 	new->maxfd = f->maxfd;
@@ -165,6 +173,7 @@
 		if((c = f->fd[i]) != nil){
 			incref(c);
 			new->fd[i] = c;
+			new->flag[i] = f->flag[i];
 		}
 	}
 	unlock(f);
@@ -194,6 +203,7 @@
 	up->closingfgrp = nil;
 
 	free(f->fd);
+	free(f->flag);
 	free(f);
 }
 
--- a/sys/src/9/port/portdat.h
+++ b/sys/src/9/port/portdat.h
@@ -125,7 +125,7 @@
 	COPEN	= 0x0001,		/* for i/o */
 	CMSG	= 0x0002,		/* the message channel for a mount */
 /*rsc	CCREATE	= 0x0004,		/* permits creation if c->mnt */
-	CCEXEC	= 0x0008,		/* close on exec */
+	CCEXEC	= 0x0008,		/* close on exec (per file descriptor) */
 	CFREE	= 0x0010,		/* not in use */
 	CRCLOSE	= 0x0020,		/* remove on close */
 	CCACHE	= 0x0080,		/* client cache */
@@ -509,6 +509,7 @@
 	Ref;
 	Lock;
 	Chan	**fd;
+	uchar	*flag;			/* per file-descriptor flags (CCEXEC) */
 	int	nfd;			/* number allocated */
 	int	maxfd;			/* highest fd in use */
 	int	exceed;			/* debugging */
--- a/sys/src/9/port/portfns.h
+++ b/sys/src/9/port/portfns.h
@@ -201,7 +201,7 @@
 void		nameerror(char*, char*);
 int		needpages(void*);
 Chan*		newchan(void);
-int		newfd(Chan*);
+int		newfd(Chan*, int);
 Mhead*		newmhead(Chan*);
 Mount*		newmount(Chan*, int, char*);
 Page*		newpage(int, Segment **, uintptr);
--- a/sys/src/9/port/sysfile.c
+++ b/sys/src/9/port/sysfile.c
@@ -25,33 +25,45 @@
 growfd(Fgrp *f, int fd)	/* fd is always >= 0 */
 {
 	Chan **newfd, **oldfd;
+	uchar *newflag, *oldflag;
+	int nfd;
 
-	if(fd < f->nfd)
+	nfd = f->nfd;
+	if(fd < nfd)
 		return 0;
-	if(fd >= f->nfd+DELTAFD)
+	if(fd >= nfd+DELTAFD)
 		return -1;	/* out of range */
 	/*
 	 * Unbounded allocation is unwise; besides, there are only 16 bits
 	 * of fid in 9P
 	 */
-	if(f->nfd >= 5000){
+	if(nfd >= 5000){
     Exhausted:
 		print("no free file descriptors\n");
 		return -1;
 	}
-	newfd = malloc((f->nfd+DELTAFD)*sizeof(Chan*));
+	oldfd = f->fd;
+	oldflag = f->flag;
+	newfd = malloc((nfd+DELTAFD)*sizeof(newfd[0]));
 	if(newfd == nil)
 		goto Exhausted;
-	oldfd = f->fd;
-	memmove(newfd, oldfd, f->nfd*sizeof(Chan*));
+	memmove(newfd, oldfd, nfd*sizeof(newfd[0]));
+	newflag = malloc((nfd+DELTAFD)*sizeof(newflag[0]));
+	if(newflag == nil){
+		free(newfd);
+		goto Exhausted;
+	}
+	memmove(newflag, oldflag, nfd*sizeof(newflag[0]));
 	f->fd = newfd;
-	free(oldfd);
-	f->nfd += DELTAFD;
+	f->flag = newflag;
+	f->nfd = nfd+DELTAFD;
 	if(fd > f->maxfd){
 		if(fd/100 > f->maxfd/100)
 			f->exceed = (fd/100)*100;
 		f->maxfd = fd;
 	}
+	free(oldfd);
+	free(oldflag);
 	return 1;
 }
 
@@ -72,9 +84,9 @@
 }
 
 int
-newfd(Chan *c)
+newfd(Chan *c, int mode)
 {
-	int fd;
+	int fd, flag;
 	Fgrp *f;
 
 	f = up->fgrp;
@@ -87,6 +99,13 @@
 	if(fd > f->maxfd)
 		f->maxfd = fd;
 	f->fd[fd] = c;
+
+	/* per file-descriptor flags */
+	flag = 0;
+	if(mode & OCEXEC)
+		flag |= CCEXEC;
+	f->flag[fd] = flag;
+
 	unlockfgrp(f);
 	return fd;
 }
@@ -112,6 +131,8 @@
 		f->maxfd = fd[1];
 	f->fd[fd[0]] = c[0];
 	f->fd[fd[1]] = c[1];
+	f->flag[fd[0]] = 0;
+	f->flag[fd[1]] = 0;
 	unlockfgrp(f);
 	return 0;
 }
@@ -247,6 +268,7 @@
 
 		oc = f->fd[fd];
 		f->fd[fd] = c;
+		f->flag[fd] = 0;
 		unlockfgrp(f);
 		if(oc != nil)
 			cclose(oc);
@@ -255,7 +277,7 @@
 			cclose(c);
 			nexterror();
 		}
-		fd = newfd(c);
+		fd = newfd(c, 0);
 		if(fd < 0)
 			error(Enofd);
 		poperror();
@@ -280,7 +302,7 @@
 		cclose(c);
 		nexterror();
 	}
-	fd = newfd(c);
+	fd = newfd(c, mode);
 	if(fd < 0)
 		error(Enofd);
 	poperror();
@@ -295,7 +317,7 @@
 
 	lock(f);
 	c = fd <= f->maxfd ? f->fd[fd] : nil;
-	if(c == nil || (flag != 0 && (c->flag&flag) == 0)){
+	if(c == nil || (flag != 0 && ((f->flag[fd]|c->flag)&flag) == 0)){
 		unlock(f);
 		return;
 	}
@@ -1166,7 +1188,7 @@
 		cclose(c);
 		nexterror();
 	}
-	fd = newfd(c);
+	fd = newfd(c, mode);
 	if(fd < 0)
 		error(Enofd);
 	poperror();