rfork(2): add RFSPAWN flag

When RFSPAWN is passed, rfork exhibits vfork(2) semantics but also resets
signal handlers in the child during creation to avoid a point of corruption
of parent state from the child.

This flag will be used by posix_spawn(3) to handle potential signal issues.

Reviewed by:	jilles, kib
Differential Revision:	https://reviews.freebsd.org/D19058
This commit is contained in:
kevans 2019-09-25 19:20:41 +00:00
parent cf4707bb2f
commit 245d8426fc
6 changed files with 53 additions and 12 deletions

View File

@ -5,7 +5,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd July 12, 2011
.Dd September 25, 2019
.Dt RFORK 2
.Os
.Sh NAME
@ -34,7 +34,9 @@ and open files.
The
.Fa flags
argument
is the logical OR of some subset of:
is either
.Dv RFSPAWN
or the logical OR of some subset of:
.Bl -tag -width ".Dv RFLINUXTHPN"
.It Dv RFPROC
If set a new process is created; otherwise changes affect the
@ -105,6 +107,14 @@ open until either they are explicitly closed
or all processes sharing the table exit.
.Pp
If
.Dv RFSPAWN
is passed,
.Nm
will use
.Xr vfork 2
semantics but reset all signal actions in the child to default.
.Pp
If
.Dv RFPROC
is set, the
value returned in the parent process

View File

@ -170,10 +170,18 @@ sys_rfork(struct thread *td, struct rfork_args *uap)
/* Don't allow kernel-only flags. */
if ((uap->flags & RFKERNELONLY) != 0)
return (EINVAL);
/* RFSPAWN must not appear with others */
if ((uap->flags & RFSPAWN) != 0 && uap->flags != RFSPAWN)
return (EINVAL);
AUDIT_ARG_FFLAGS(uap->flags);
bzero(&fr, sizeof(fr));
fr.fr_flags = uap->flags;
if ((uap->flags & RFSPAWN) != 0) {
fr.fr_flags = RFFDG | RFPROC | RFPPWAIT | RFMEM;
fr.fr_flags2 = FR2_DROPSIG_CAUGHT;
} else {
fr.fr_flags = uap->flags;
}
fr.fr_pidp = &pid;
error = fork1(td, &fr);
if (error == 0) {
@ -471,6 +479,11 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
} else {
sigacts_copy(newsigacts, p1->p_sigacts);
p2->p_sigacts = newsigacts;
if ((fr->fr_flags2 & FR2_DROPSIG_CAUGHT) != 0) {
mtx_lock(&p2->p_sigacts->ps_mtx);
sig_drop_caught(p2);
mtx_unlock(&p2->p_sigacts->ps_mtx);
}
}
if (fr->fr_flags & RFTSIGZMB)

View File

@ -986,12 +986,7 @@ execsigs(struct proc *p)
PROC_LOCK_ASSERT(p, MA_OWNED);
ps = p->p_sigacts;
mtx_lock(&ps->ps_mtx);
while (SIGNOTEMPTY(ps->ps_sigcatch)) {
sig = sig_ffs(&ps->ps_sigcatch);
sigdflt(ps, sig);
if ((sigprop(sig) & SIGPROP_IGNORE) != 0)
sigqueue_delete_proc(p, sig);
}
sig_drop_caught(p);
/*
* As CloudABI processes cannot modify signal handlers, fully
@ -3857,3 +3852,20 @@ sigacts_shared(struct sigacts *ps)
return (ps->ps_refcnt > 1);
}
void
sig_drop_caught(struct proc *p)
{
int sig;
struct sigacts *ps;
ps = p->p_sigacts;
PROC_LOCK_ASSERT(p, MA_OWNED);
mtx_assert(&ps->ps_mtx, MA_OWNED);
while (SIGNOTEMPTY(ps->ps_sigcatch)) {
sig = sig_ffs(&ps->ps_sigcatch);
sigdflt(ps, sig);
if ((sigprop(sig) & SIGPROP_IGNORE) != 0)
sigqueue_delete_proc(p, sig);
}
}

View File

@ -1006,6 +1006,8 @@ struct fork_req {
int *fr_pd_fd;
int fr_pd_flags;
struct filecaps *fr_pd_fcaps;
int fr_flags2;
#define FR2_DROPSIG_CAUGHT 0x00001 /* Drop caught non-DFL signals */
};
/*

View File

@ -381,6 +381,7 @@ void sigacts_copy(struct sigacts *dest, struct sigacts *src);
void sigacts_free(struct sigacts *ps);
struct sigacts *sigacts_hold(struct sigacts *ps);
int sigacts_shared(struct sigacts *ps);
void sig_drop_caught(struct proc *p);
void sigexit(struct thread *td, int sig) __dead2;
int sigev_findtd(struct proc *p, struct sigevent *sigev, struct thread **);
int sig_ffs(sigset_t *set);

View File

@ -188,11 +188,14 @@
#define RFTSIGNUM(flags) (((flags) >> RFTSIGSHIFT) & RFTSIGMASK)
#define RFTSIGFLAGS(signum) ((signum) << RFTSIGSHIFT)
#define RFPROCDESC (1<<28) /* return a process descriptor */
#define RFPPWAIT (1<<31) /* parent sleeps until child exits (vfork) */
/* kernel: parent sleeps until child exits (vfork) */
#define RFPPWAIT (1<<31)
/* user: vfork(2) semantics, clear signals */
#define RFSPAWN (1U<<31)
#define RFFLAGS (RFFDG | RFPROC | RFMEM | RFNOWAIT | RFCFDG | \
RFTHREAD | RFSIGSHARE | RFLINUXTHPN | RFSTOPPED | RFHIGHPID | RFTSIGZMB | \
RFPROCDESC | RFPPWAIT)
#define RFKERNELONLY (RFSTOPPED | RFHIGHPID | RFPPWAIT | RFPROCDESC)
RFPROCDESC | RFSPAWN | RFPPWAIT)
#define RFKERNELONLY (RFSTOPPED | RFHIGHPID | RFPROCDESC)
#endif /* __BSD_VISIBLE */