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.
This commit is contained in:
cinap_lenrek 2020-12-13 16:04:09 +01:00
parent b2b2d2cb4c
commit 0b33b3b8ad
11 changed files with 74 additions and 30 deletions

View file

@ -32,6 +32,19 @@ requires that
.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

View file

@ -97,13 +97,12 @@ sysfauth(va_list list)
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;
}

View file

@ -1468,9 +1468,6 @@ namec(char *aname, int amode, int omode, ulong perm)
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 @@ namec(char *aname, int amode, int omode, ulong perm)
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;

View file

@ -63,6 +63,8 @@ dupopen(Chan *c, int omode)
Chan *f;
int fd, twicefd;
if(omode & ORCLOSE)
error(Eperm);
if(c->qid.type & QTDIR){
if(omode != 0)
error(Eisdir);

View file

@ -396,6 +396,8 @@ shropen(Chan *c, int omode)
case Qcmpt:
if(omode&OTRUNC)
error(Eexist);
if(omode&ORCLOSE)
error(Eperm);
shr = sch->shr;
mpt = sch->mpt;
devpermcheck(mpt->owner, mpt->perm, mode);

View file

@ -135,6 +135,8 @@ srvopen(Chan *c, int omode)
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 @@ srvwrite(Chan *c, void *va, long n, vlong)
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);

View file

@ -176,7 +176,7 @@ extern void qsort(void*, long, long, int (*)(void*, void*));
#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 */

View file

@ -140,7 +140,8 @@ dupfgrp(Fgrp *f)
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 @@ dupfgrp(Fgrp *f)
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 @@ dupfgrp(Fgrp *f)
if((c = f->fd[i]) != nil){
incref(c);
new->fd[i] = c;
new->flag[i] = f->flag[i];
}
}
unlock(f);
@ -194,6 +203,7 @@ closefgrp(Fgrp *f)
up->closingfgrp = nil;
free(f->fd);
free(f->flag);
free(f);
}

View file

@ -125,7 +125,7 @@ enum
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 @@ struct Fgrp
Ref;
Lock;
Chan **fd;
uchar *flag; /* per file-descriptor flags (CCEXEC) */
int nfd; /* number allocated */
int maxfd; /* highest fd in use */
int exceed; /* debugging */

View file

@ -201,7 +201,7 @@ Chan* namec(char*, int, int, ulong);
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);

View file

@ -25,33 +25,45 @@ int
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 @@ findfreefd(Fgrp *f, int start)
}
int
newfd(Chan *c)
newfd(Chan *c, int mode)
{
int fd;
int fd, flag;
Fgrp *f;
f = up->fgrp;
@ -87,6 +99,13 @@ newfd(Chan *c)
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 @@ newfd2(int fd[2], Chan *c[2])
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 @@ sysdup(va_list list)
oc = f->fd[fd];
f->fd[fd] = c;
f->flag[fd] = 0;
unlockfgrp(f);
if(oc != nil)
cclose(oc);
@ -255,7 +277,7 @@ sysdup(va_list list)
cclose(c);
nexterror();
}
fd = newfd(c);
fd = newfd(c, 0);
if(fd < 0)
error(Enofd);
poperror();
@ -280,7 +302,7 @@ sysopen(va_list list)
cclose(c);
nexterror();
}
fd = newfd(c);
fd = newfd(c, mode);
if(fd < 0)
error(Enofd);
poperror();
@ -295,7 +317,7 @@ fdclose(int fd, int flag)
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 @@ syscreate(va_list list)
cclose(c);
nexterror();
}
fd = newfd(c);
fd = newfd(c, mode);
if(fd < 0)
error(Enofd);
poperror();