On exec, single-threading must be enforced before arguments space is

allocated from exec_map.  If many threads try to perform execve(2) in
parallel, the exec map is exhausted and some threads sleep
uninterruptible waiting for the map space.  Then, the thread which won
the race for the space allocation, cannot single-thread the process,
causing deadlock.

Reported and tested by:	pho (previous version)
Sponsored by:	The FreeBSD Foundation
MFC after:	2 weeks
This commit is contained in:
Konstantin Belousov 2015-05-10 09:00:40 +00:00
parent 70ad407c86
commit 7b445033ff
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=282708
7 changed files with 119 additions and 51 deletions

View File

@ -137,6 +137,7 @@ int
linux_execve(struct thread *td, struct linux_execve_args *args)
{
struct image_args eargs;
struct vmspace *oldvmspace;
char *path;
int error;
@ -147,12 +148,17 @@ linux_execve(struct thread *td, struct linux_execve_args *args)
printf(ARGS(execve, "%s"), path);
#endif
error = pre_execve(td, &oldvmspace);
if (error != 0) {
free(path, M_TEMP);
return (error);
}
error = freebsd32_exec_copyin_args(&eargs, path, UIO_SYSSPACE,
args->argp, args->envp);
free(path, M_TEMP);
if (error == 0)
error = kern_execve(td, &eargs, NULL);
if (error == 0)
if (error == 0) {
/* Linux process can execute FreeBSD one, do not attempt
* to create emuldata for such process using
* linux_proc_init, this leads to a panic on KASSERT
@ -160,6 +166,8 @@ linux_execve(struct thread *td, struct linux_execve_args *args)
*/
if (SV_PROC_ABI(td->td_proc) == SV_ABI_LINUX)
error = linux_proc_init(td, 0, 0);
}
post_execve(td, error, oldvmspace);
return (error);
}

View File

@ -402,12 +402,17 @@ int
freebsd32_execve(struct thread *td, struct freebsd32_execve_args *uap)
{
struct image_args eargs;
struct vmspace *oldvmspace;
int error;
error = pre_execve(td, &oldvmspace);
if (error != 0)
return (error);
error = freebsd32_exec_copyin_args(&eargs, uap->fname, UIO_USERSPACE,
uap->argv, uap->envv);
if (error == 0)
error = kern_execve(td, &eargs, NULL);
post_execve(td, error, oldvmspace);
return (error);
}
@ -415,14 +420,19 @@ int
freebsd32_fexecve(struct thread *td, struct freebsd32_fexecve_args *uap)
{
struct image_args eargs;
struct vmspace *oldvmspace;
int error;
error = pre_execve(td, &oldvmspace);
if (error != 0)
return (error);
error = freebsd32_exec_copyin_args(&eargs, NULL, UIO_SYSSPACE,
uap->argv, uap->envv);
if (error == 0) {
eargs.fd = uap->fd;
error = kern_execve(td, &eargs, NULL);
}
post_execve(td, error, oldvmspace);
return (error);
}

View File

@ -167,15 +167,22 @@ svr4_sys_execv(td, uap)
struct svr4_sys_execv_args *uap;
{
struct image_args eargs;
struct vmspace *oldvmspace;
char *path;
int error;
CHECKALTEXIST(td, uap->path, &path);
error = pre_execve(td, &oldvmspace);
if (error != 0) {
free(path, M_TEMP);
return (error);
}
error = exec_copyin_args(&eargs, path, UIO_SYSSPACE, uap->argp, NULL);
free(path, M_TEMP);
if (error == 0)
error = kern_execve(td, &eargs, NULL);
post_execve(td, error, oldvmspace);
return (error);
}
@ -185,16 +192,23 @@ svr4_sys_execve(td, uap)
struct svr4_sys_execve_args *uap;
{
struct image_args eargs;
struct vmspace *oldvmspace;
char *path;
int error;
CHECKALTEXIST(td, uap->path, &path);
error = pre_execve(td, &oldvmspace);
if (error != 0) {
free(path, M_TEMP);
return (error);
}
error = exec_copyin_args(&eargs, path, UIO_SYSSPACE, uap->argp,
uap->envp);
free(path, M_TEMP);
if (error == 0)
error = kern_execve(td, &eargs, NULL);
post_execve(td, error, oldvmspace);
return (error);
}

View File

@ -200,15 +200,22 @@ ibcs2_execv(td, uap)
struct ibcs2_execv_args *uap;
{
struct image_args eargs;
struct vmspace *oldvmspace;
char *path;
int error;
CHECKALTEXIST(td, uap->path, &path);
error = pre_execve(td, &oldvmspace);
if (error != 0) {
free(path, M_TEMP);
return (error);
}
error = exec_copyin_args(&eargs, path, UIO_SYSSPACE, uap->argp, NULL);
free(path, M_TEMP);
if (error == 0)
error = kern_execve(td, &eargs, NULL);
post_execve(td, error, oldvmspace);
return (error);
}
@ -218,16 +225,23 @@ ibcs2_execve(td, uap)
struct ibcs2_execve_args *uap;
{
struct image_args eargs;
struct vmspace *oldvmspace;
char *path;
int error;
CHECKALTEXIST(td, uap->path, &path);
error = pre_execve(td, &oldvmspace);
if (error != 0) {
free(path, M_TEMP);
return (error);
}
error = exec_copyin_args(&eargs, path, UIO_SYSSPACE, uap->argp,
uap->envp);
free(path, M_TEMP);
if (error == 0)
error = kern_execve(td, &eargs, NULL);
post_execve(td, error, oldvmspace);
return (error);
}

View File

@ -126,9 +126,10 @@ bsd_to_linux_sigaltstack(int bsa)
int
linux_execve(struct thread *td, struct linux_execve_args *args)
{
int error;
char *newpath;
struct image_args eargs;
struct vmspace *oldvmspace;
char *newpath;
int error;
LCONVPATHEXIST(td, args->path, &newpath);
@ -137,12 +138,17 @@ linux_execve(struct thread *td, struct linux_execve_args *args)
printf(ARGS(execve, "%s"), newpath);
#endif
error = pre_execve(td, &oldvmspace);
if (error != 0) {
free(newpath, M_TEMP);
return (error);
}
error = exec_copyin_args(&eargs, newpath, UIO_SYSSPACE,
args->argp, args->envp);
free(newpath, M_TEMP);
if (error == 0)
error = kern_execve(td, &eargs, NULL);
if (error == 0)
if (error == 0) {
/* linux process can exec fbsd one, dont attempt
* to create emuldata for such process using
* linux_proc_init, this leads to a panic on KASSERT
@ -150,6 +156,8 @@ linux_execve(struct thread *td, struct linux_execve_args *args)
*/
if (SV_PROC_ABI(td->td_proc) == SV_ABI_LINUX)
error = linux_proc_init(td, 0, 0);
}
post_execve(td, error, oldvmspace);
return (error);
}

View File

@ -193,21 +193,20 @@ struct execve_args {
#endif
int
sys_execve(td, uap)
struct thread *td;
struct execve_args /* {
char *fname;
char **argv;
char **envv;
} */ *uap;
sys_execve(struct thread *td, struct execve_args *uap)
{
int error;
struct image_args args;
struct vmspace *oldvmspace;
int error;
error = pre_execve(td, &oldvmspace);
if (error != 0)
return (error);
error = exec_copyin_args(&args, uap->fname, UIO_USERSPACE,
uap->argv, uap->envv);
if (error == 0)
error = kern_execve(td, &args, NULL);
post_execve(td, error, oldvmspace);
return (error);
}
@ -221,15 +220,20 @@ struct fexecve_args {
int
sys_fexecve(struct thread *td, struct fexecve_args *uap)
{
int error;
struct image_args args;
struct vmspace *oldvmspace;
int error;
error = pre_execve(td, &oldvmspace);
if (error != 0)
return (error);
error = exec_copyin_args(&args, NULL, UIO_SYSSPACE,
uap->argv, uap->envv);
if (error == 0) {
args.fd = uap->fd;
error = kern_execve(td, &args, NULL);
}
post_execve(td, error, oldvmspace);
return (error);
}
@ -243,65 +247,56 @@ struct __mac_execve_args {
#endif
int
sys___mac_execve(td, uap)
struct thread *td;
struct __mac_execve_args /* {
char *fname;
char **argv;
char **envv;
struct mac *mac_p;
} */ *uap;
sys___mac_execve(struct thread *td, struct __mac_execve_args *uap)
{
#ifdef MAC
int error;
struct image_args args;
struct vmspace *oldvmspace;
int error;
error = pre_execve(td, &oldvmspace);
if (error != 0)
return (error);
error = exec_copyin_args(&args, uap->fname, UIO_USERSPACE,
uap->argv, uap->envv);
if (error == 0)
error = kern_execve(td, &args, uap->mac_p);
post_execve(td, error, oldvmspace);
return (error);
#else
return (ENOSYS);
#endif
}
/*
* XXX: kern_execve has the astonishing property of not always returning to
* the caller. If sufficiently bad things happen during the call to
* do_execve(), it can end up calling exit1(); as a result, callers must
* avoid doing anything which they might need to undo (e.g., allocating
* memory).
*/
int
kern_execve(td, args, mac_p)
struct thread *td;
struct image_args *args;
struct mac *mac_p;
pre_execve(struct thread *td, struct vmspace **oldvmspace)
{
struct proc *p = td->td_proc;
struct vmspace *oldvmspace;
struct proc *p;
int error;
AUDIT_ARG_ARGV(args->begin_argv, args->argc,
args->begin_envv - args->begin_argv);
AUDIT_ARG_ENVV(args->begin_envv, args->envc,
args->endp - args->begin_envv);
if (p->p_flag & P_HADTHREADS) {
KASSERT(td == curthread, ("non-current thread %p", td));
error = 0;
p = td->td_proc;
if ((p->p_flag & P_HADTHREADS) != 0) {
PROC_LOCK(p);
if (thread_single(p, SINGLE_BOUNDARY)) {
PROC_UNLOCK(p);
exec_free_args(args);
return (ERESTART); /* Try again later. */
}
if (thread_single(p, SINGLE_BOUNDARY) != 0)
error = ERESTART;
PROC_UNLOCK(p);
}
KASSERT(error != 0 || (td->td_pflags & TDP_EXECVMSPC) == 0,
("nested execve"));
*oldvmspace = p->p_vmspace;
return (error);
}
KASSERT((td->td_pflags & TDP_EXECVMSPC) == 0, ("nested execve"));
oldvmspace = td->td_proc->p_vmspace;
error = do_execve(td, args, mac_p);
void
post_execve(struct thread *td, int error, struct vmspace *oldvmspace)
{
struct proc *p;
if (p->p_flag & P_HADTHREADS) {
KASSERT(td == curthread, ("non-current thread %p", td));
p = td->td_proc;
if ((p->p_flag & P_HADTHREADS) != 0) {
PROC_LOCK(p);
/*
* If success, we upgrade to SINGLE_EXIT state to
@ -314,13 +309,29 @@ kern_execve(td, args, mac_p)
PROC_UNLOCK(p);
}
if ((td->td_pflags & TDP_EXECVMSPC) != 0) {
KASSERT(td->td_proc->p_vmspace != oldvmspace,
KASSERT(p->p_vmspace != oldvmspace,
("oldvmspace still used"));
vmspace_free(oldvmspace);
td->td_pflags &= ~TDP_EXECVMSPC;
}
}
return (error);
/*
* XXX: kern_execve has the astonishing property of not always returning to
* the caller. If sufficiently bad things happen during the call to
* do_execve(), it can end up calling exit1(); as a result, callers must
* avoid doing anything which they might need to undo (e.g., allocating
* memory).
*/
int
kern_execve(struct thread *td, struct image_args *args, struct mac *mac_p)
{
AUDIT_ARG_ARGV(args->begin_argv, args->argc,
args->begin_envv - args->begin_argv);
AUDIT_ARG_ENVV(args->begin_envv, args->envc,
args->endp - args->begin_envv);
return (do_execve(td, args, mac_p));
}
/*

View File

@ -86,6 +86,7 @@ struct image_params {
#ifdef _KERNEL
struct sysentvec;
struct thread;
struct vmspace;
#define IMGACT_CORE_COMPRESS 0x01
@ -98,6 +99,8 @@ void exec_setregs(struct thread *, struct image_params *, u_long);
int exec_shell_imgact(struct image_params *);
int exec_copyin_args(struct image_args *, char *, enum uio_seg,
char **, char **);
int pre_execve(struct thread *td, struct vmspace **oldvmspace);
void post_execve(struct thread *td, int error, struct vmspace *oldvmspace);
#endif
#endif /* !_SYS_IMGACT_H_ */