Changeset View
Standalone View
sys/kern/kern_exec.c
Show First 20 Lines • Show All 235 Lines • ▼ Show 20 Lines | ||||||||||
/* | /* | |||||||||
* Each of the items is a pointer to a `const struct execsw', hence the | * Each of the items is a pointer to a `const struct execsw', hence the | |||||||||
* double pointer here. | * double pointer here. | |||||||||
*/ | */ | |||||||||
static const struct execsw **execsw; | static const struct execsw **execsw; | |||||||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | |||||||||
struct execve_args { | struct execve_args { | |||||||||
char *fname; | char *fname; | |||||||||
char **argv; | char **argv; | |||||||||
char **envv; | char **envv; | |||||||||
}; | }; | |||||||||
#endif | #endif | |||||||||
int | int | |||||||||
sys_execve(struct thread *td, struct execve_args *uap) | sys_execve(struct thread *td, struct execve_args *uap) | |||||||||
{ | { | |||||||||
struct image_args args; | struct image_args args; | |||||||||
struct vmspace *oldvmspace; | struct vmspace *oldvmspace; | |||||||||
▲ Show 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | ||||||||||
#endif | #endif | |||||||||
int error, i, orig_osrel; | int error, i, orig_osrel; | |||||||||
uint32_t orig_fctl0; | uint32_t orig_fctl0; | |||||||||
Elf_Brandinfo *orig_brandinfo; | Elf_Brandinfo *orig_brandinfo; | |||||||||
static const char fexecv_proc_title[] = "(fexecv)"; | static const char fexecv_proc_title[] = "(fexecv)"; | |||||||||
imgp = &image_params; | imgp = &image_params; | |||||||||
#ifdef KTRACE | #ifdef KTRACE | |||||||||
kiop = NULL; | kiop = NULL; | |||||||||
markj: Initializing oldtextdvp twice. I think it is technically safe to leave newtextvp uninitialized. | ||||||||||
Done Inline ActionsPerhaps yes, but following the mix of success/failure paths on the function exit is risky. kib: Perhaps yes, but following the mix of success/failure paths on the function exit is risky. | ||||||||||
Done Inline ActionsI prefer the explicit initialization as well. I just wasn't sure if it was intentional. markj: I prefer the explicit initialization as well. I just wasn't sure if it was intentional. | ||||||||||
#endif | #endif | |||||||||
/* | /* | |||||||||
* Lock the process and set the P_INEXEC flag to indicate that | * Lock the process and set the P_INEXEC flag to indicate that | |||||||||
* it should be left alone until we're done here. This is | * it should be left alone until we're done here. This is | |||||||||
* necessary to avoid race conditions - e.g. in ptrace() - | * necessary to avoid race conditions - e.g. in ptrace() - | |||||||||
* that might allow a local user to illicitly obtain elevated | * that might allow a local user to illicitly obtain elevated | |||||||||
* privileges. | * privileges. | |||||||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | #endif | |||||||||
newtextvp = nd.ni_vp; | newtextvp = nd.ni_vp; | |||||||||
imgp->vp = newtextvp; | imgp->vp = newtextvp; | |||||||||
} else { | } else { | |||||||||
AUDIT_ARG_FD(args->fd); | AUDIT_ARG_FD(args->fd); | |||||||||
/* | /* | |||||||||
* Descriptors opened only with O_EXEC or O_RDONLY are allowed. | * Descriptors opened only with O_EXEC or O_RDONLY are allowed. | |||||||||
*/ | */ | |||||||||
error = fgetvp_exec(td, args->fd, &cap_fexecve_rights, &newtextvp); | error = fgetvp_exec(td, args->fd, &cap_fexecve_rights, | |||||||||
&newtextvp); | ||||||||||
if (error) | if (error) | |||||||||
goto exec_fail; | goto exec_fail; | |||||||||
vn_lock(newtextvp, LK_SHARED | LK_RETRY); | vn_lock(newtextvp, LK_SHARED | LK_RETRY); | |||||||||
AUDIT_ARG_VNODE1(newtextvp); | AUDIT_ARG_VNODE1(newtextvp); | |||||||||
imgp->vp = newtextvp; | imgp->vp = newtextvp; | |||||||||
} | } | |||||||||
/* | /* | |||||||||
▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | #endif | |||||||||
/* | /* | |||||||||
* Do the best to calculate the full path to the image file. | * Do the best to calculate the full path to the image file. | |||||||||
*/ | */ | |||||||||
if (args->fname != NULL && args->fname[0] == '/') | if (args->fname != NULL && args->fname[0] == '/') | |||||||||
imgp->execpath = args->fname; | imgp->execpath = args->fname; | |||||||||
else { | else { | |||||||||
VOP_UNLOCK(imgp->vp); | VOP_UNLOCK(imgp->vp); | |||||||||
if (vn_fullpath(imgp->vp, &imgp->execpath, &imgp->freepath) != 0) | if (vn_fullpath(imgp->vp, &imgp->execpath, | |||||||||
&imgp->freepath) != 0) | ||||||||||
imgp->execpath = args->fname; | imgp->execpath = args->fname; | |||||||||
vn_lock(imgp->vp, LK_SHARED | LK_RETRY); | vn_lock(imgp->vp, LK_SHARED | LK_RETRY); | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* If the current process has a special image activator it | * If the current process has a special image activator it | |||||||||
* wants to try first, call it. For example, emulating shell | * wants to try first, call it. For example, emulating shell | |||||||||
* scripts differently. | * scripts differently. | |||||||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | if (args->fname != NULL) | |||||||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | |||||||||
#ifdef MAC | #ifdef MAC | |||||||||
mac_execve_interpreter_enter(newtextvp, &interpvplabel); | mac_execve_interpreter_enter(newtextvp, &interpvplabel); | |||||||||
#endif | #endif | |||||||||
if (imgp->opened) { | if (imgp->opened) { | |||||||||
VOP_CLOSE(newtextvp, FREAD, td->td_ucred, td); | VOP_CLOSE(newtextvp, FREAD, td->td_ucred, td); | |||||||||
imgp->opened = 0; | imgp->opened = 0; | |||||||||
} | } | |||||||||
vput(newtextvp); | vput(newtextvp); | |||||||||
Done Inline ActionsIt took me a while to convince myself that we do not need to set imgp->vp = NULL here, but maybe it would be worthwhile anyway. markj: It took me a while to convince myself that we do not need to set `imgp->vp = NULL` here, but… | ||||||||||
Done Inline Actionsimgp is fully reset on interpreter recycle, I will add the reset. kib: imgp is fully reset on interpreter recycle, I will add the reset. | ||||||||||
vm_object_deallocate(imgp->object); | vm_object_deallocate(imgp->object); | |||||||||
imgp->object = NULL; | imgp->object = NULL; | |||||||||
Done Inline ActionsDon't we unconditionally set nd.ni_dvp = NULL above? i.e. the dvp reference is unconditionally transferred to newtextdvp after the namei() call. markj: Don't we unconditionally set `nd.ni_dvp = NULL` above? i.e. the dvp reference is… | ||||||||||
Done Inline ActionsSo yes this is true there, I changed the vrele to assert that ni_dvp == NULL. I want the vrele(ni_dvp) to be left as is on exec_fail_dealloc: label. I think that technically ni_dvp cannot be non-null there, but it is too fragile to assume on the recovery path. kib: So yes this is true there, I changed the vrele to assert that ni_dvp == NULL.
I want the vrele… | ||||||||||
Done Inline ActionsActually I do not understand how this works. Doesn't the use of vn_fullpath_hardlink() assume that ni_dvp != NULL? markj: Actually I do not understand how this works. Doesn't the use of vn_fullpath_hardlink() assume… | ||||||||||
Done Inline ActionsRight, there were too many moves around. So I did one more, lets calculate the execpath right after namei(). I like the move on its own, because it naturally fit into fexecve(2) path of calculating execpath, also removing one vnode relock. Then ni_dvp is not used anywhere except this args->fname != NULL path. Also I think it is possible to eliminate second NDINIT() by moving first one under the same if() branch. kib: Right, there were too many moves around.
So I did one more, lets calculate the execpath right… | ||||||||||
execve_nosetid(imgp); | execve_nosetid(imgp); | |||||||||
imgp->execpath = NULL; | imgp->execpath = NULL; | |||||||||
free(imgp->freepath, M_TEMP); | free(imgp->freepath, M_TEMP); | |||||||||
imgp->freepath = NULL; | imgp->freepath = NULL; | |||||||||
/* set new name to that of the interpreter */ | /* set new name to that of the interpreter */ | |||||||||
NDINIT(&nd, LOOKUP, ISOPEN | LOCKLEAF | LOCKSHARED | FOLLOW | | NDINIT(&nd, LOOKUP, ISOPEN | LOCKLEAF | LOCKSHARED | FOLLOW | | |||||||||
SAVENAME, UIO_SYSSPACE, imgp->interpreter_name, td); | SAVENAME, UIO_SYSSPACE, imgp->interpreter_name, td); | |||||||||
args->fname = imgp->interpreter_name; | args->fname = imgp->interpreter_name; | |||||||||
▲ Show 20 Lines • Show All 154 Lines • ▼ Show 20 Lines | if (imgp->newcred != NULL) { | |||||||||
proc_set_cred(p, imgp->newcred); | proc_set_cred(p, imgp->newcred); | |||||||||
crfree(oldcred); | crfree(oldcred); | |||||||||
oldcred = NULL; | oldcred = NULL; | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* Store the vp for use in procfs. This vnode was referenced by namei | * Store the vp for use in procfs. This vnode was referenced by namei | |||||||||
* or fgetvp_exec. | * or fgetvp_exec. | |||||||||
*/ | */ | |||||||||
Done Inline Actions
markj: | ||||||||||
oldtextvp = p->p_textvp; | oldtextvp = p->p_textvp; | |||||||||
p->p_textvp = newtextvp; | p->p_textvp = newtextvp; | |||||||||
#ifdef KDTRACE_HOOKS | #ifdef KDTRACE_HOOKS | |||||||||
/* | /* | |||||||||
* Tell the DTrace fasttrap provider about the exec if it | * Tell the DTrace fasttrap provider about the exec if it | |||||||||
* has declared an interest. | * has declared an interest. | |||||||||
*/ | */ | |||||||||
▲ Show 20 Lines • Show All 163 Lines • ▼ Show 20 Lines | #if VM_NRESERVLEVEL > 0 | |||||||||
if ((object->flags & OBJ_COLORED) == 0) { | if ((object->flags & OBJ_COLORED) == 0) { | |||||||||
VM_OBJECT_WLOCK(object); | VM_OBJECT_WLOCK(object); | |||||||||
vm_object_color(object, 0); | vm_object_color(object, 0); | |||||||||
VM_OBJECT_WUNLOCK(object); | VM_OBJECT_WUNLOCK(object); | |||||||||
} | } | |||||||||
#endif | #endif | |||||||||
error = vm_page_grab_valid_unlocked(&m, object, 0, | error = vm_page_grab_valid_unlocked(&m, object, 0, | |||||||||
VM_ALLOC_COUNT(VM_INITIAL_PAGEIN) | | VM_ALLOC_COUNT(VM_INITIAL_PAGEIN) | | |||||||||
VM_ALLOC_NORMAL | VM_ALLOC_NOBUSY | VM_ALLOC_WIRED); | VM_ALLOC_NORMAL | VM_ALLOC_NOBUSY | VM_ALLOC_WIRED); | |||||||||
if (error != VM_PAGER_OK) | if (error != VM_PAGER_OK) | |||||||||
return (EIO); | return (EIO); | |||||||||
imgp->firstpage = sf_buf_alloc(m, 0); | imgp->firstpage = sf_buf_alloc(m, 0); | |||||||||
imgp->image_header = (char *)sf_buf_kva(imgp->firstpage); | imgp->image_header = (char *)sf_buf_kva(imgp->firstpage); | |||||||||
return (0); | return (0); | |||||||||
} | } | |||||||||
▲ Show 20 Lines • Show All 928 Lines • ▼ Show 20 Lines | sbuf_drain_core_output(void *arg, const char *data, int len) | |||||||||
* those routines when dumping a live process. In our case we | * those routines when dumping a live process. In our case we | |||||||||
* can safely release the lock before draining and acquire | * can safely release the lock before draining and acquire | |||||||||
* again after. | * again after. | |||||||||
*/ | */ | |||||||||
locked = PROC_LOCKED(p); | locked = PROC_LOCKED(p); | |||||||||
if (locked) | if (locked) | |||||||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | |||||||||
if (cp->comp != NULL) | if (cp->comp != NULL) | |||||||||
error = compressor_write(cp->comp, __DECONST(char *, data), len); | error = compressor_write(cp->comp, __DECONST(char *, data), | |||||||||
len); | ||||||||||
else | else | |||||||||
error = core_write(cp, __DECONST(void *, data), len, cp->offset, | error = core_write(cp, __DECONST(void *, data), len, cp->offset, | |||||||||
UIO_SYSSPACE, NULL); | UIO_SYSSPACE, NULL); | |||||||||
if (locked) | if (locked) | |||||||||
PROC_LOCK(p); | PROC_LOCK(p); | |||||||||
if (error != 0) | if (error != 0) | |||||||||
return (-error); | return (-error); | |||||||||
cp->offset += len; | cp->offset += len; | |||||||||
return (len); | return (len); | |||||||||
} | } |
Initializing oldtextdvp twice. I think it is technically safe to leave newtextvp uninitialized.