netbsd32_execve.c revision 1.6
1/* $NetBSD: netbsd32_execve.c,v 1.6 2001/11/13 02:09:05 lukem Exp $ */ 2 3/* 4 * Copyright (c) 1998, 2001 Matthew R. Green 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32__KERNEL_RCSID(0, "$NetBSD: netbsd32_execve.c,v 1.6 2001/11/13 02:09:05 lukem Exp $"); 33 34#if defined(_KERNEL_OPT) 35#include "opt_ktrace.h" 36#endif 37 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/malloc.h> 41#include <sys/mount.h> 42#include <sys/stat.h> 43#include <sys/wait.h> 44#include <sys/ktrace.h> 45#include <sys/vnode.h> 46#include <sys/file.h> 47#include <sys/filedesc.h> 48#include <sys/namei.h> 49 50#include <uvm/uvm_extern.h> 51 52#include <sys/syscallargs.h> 53#include <sys/proc.h> 54#include <sys/acct.h> 55#include <sys/exec.h> 56 57#include <compat/netbsd32/netbsd32.h> 58#include <compat/netbsd32/netbsd32_syscall.h> 59#include <compat/netbsd32/netbsd32_syscallargs.h> 60 61/* this is provided by kern/kern_exec.c */ 62extern int exec_maxhdrsz; 63extern struct lock exec_lock; 64 65/* 66 * Need to completly reimplement this syscall due to argument copying. 67 */ 68/* ARGSUSED */ 69int 70netbsd32_execve(p, v, retval) 71 struct proc *p; 72 void *v; 73 register_t *retval; 74{ 75 struct netbsd32_execve_args /* { 76 syscallarg(const netbsd32_charp) path; 77 syscallarg(netbsd32_charpp) argp; 78 syscallarg(netbsd32_charpp) envp; 79 } */ *uap = v; 80 struct sys_execve_args ua; 81 caddr_t sg; 82 83 NETBSD32TOP_UAP(path, const char); 84 NETBSD32TOP_UAP(argp, char *); 85 NETBSD32TOP_UAP(envp, char *); 86 sg = stackgap_init(p->p_emul); 87 CHECK_ALT_EXIST(p, &sg, SCARG(&ua, path)); 88 89 return netbsd32_execve2(p, &ua, retval); 90} 91 92int 93netbsd32_execve2(p, uap, retval) 94 struct proc *p; 95 struct sys_execve_args *uap; 96 register_t *retval; 97{ 98 /* Function args */ 99 int error, i; 100 struct exec_package pack; 101 struct nameidata nid; 102 struct vattr attr; 103 struct ucred *cred = p->p_ucred; 104 char *argp; 105 netbsd32_charp const *cpp; 106 char *dp; 107 netbsd32_charp sp; 108 long argc, envc; 109 size_t len; 110 char *stack; 111 struct ps_strings arginfo; 112 struct vmspace *vm; 113 char **tmpfap; 114 int szsigcode; 115 struct exec_vmcmd *base_vcp = NULL; 116 117 /* 118 * Init the namei data to point the file user's program name. 119 * This is done here rather than in check_exec(), so that it's 120 * possible to override this settings if any of makecmd/probe 121 * functions call check_exec() recursively - for example, 122 * see exec_script_makecmds(). 123 */ 124 NDINIT(&nid, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); 125 126 /* 127 * initialize the fields of the exec package. 128 */ 129 pack.ep_name = SCARG(uap, path); 130 pack.ep_hdr = malloc(exec_maxhdrsz, M_EXEC, M_WAITOK); 131 pack.ep_hdrlen = exec_maxhdrsz; 132 pack.ep_hdrvalid = 0; 133 pack.ep_ndp = &nid; 134 pack.ep_emul_arg = NULL; 135 pack.ep_vmcmds.evs_cnt = 0; 136 pack.ep_vmcmds.evs_used = 0; 137 pack.ep_vap = &attr; 138 pack.ep_flags = 0; 139 140 lockmgr(&exec_lock, LK_SHARED, NULL); 141 142 /* see if we can run it. */ 143 if ((error = check_exec(p, &pack)) != 0) 144 goto freehdr; 145 146 /* XXX -- THE FOLLOWING SECTION NEEDS MAJOR CLEANUP */ 147 148 /* allocate an argument buffer */ 149 argp = (char *) uvm_km_valloc_wait(exec_map, NCARGS); 150#ifdef DIAGNOSTIC 151 if (argp == (vaddr_t) 0) 152 panic("netbsd32_execve: argp == NULL"); 153#endif 154 dp = argp; 155 argc = 0; 156 157 /* copy the fake args list, if there's one, freeing it as we go */ 158 if (pack.ep_flags & EXEC_HASARGL) { 159 tmpfap = pack.ep_fa; 160 while (*tmpfap != NULL) { 161 char *cp; 162 163 cp = *tmpfap; 164 while (*cp) 165 *dp++ = *cp++; 166 dp++; 167 168 FREE(*tmpfap, M_EXEC); 169 tmpfap++; argc++; 170 } 171 FREE(pack.ep_fa, M_EXEC); 172 pack.ep_flags &= ~EXEC_HASARGL; 173 } 174 175 /* Now get argv & environment */ 176 if (!(cpp = (netbsd32_charp *)SCARG(uap, argp))) { 177 error = EINVAL; 178 goto bad; 179 } 180 181 if (pack.ep_flags & EXEC_SKIPARG) 182 cpp++; 183 184 while (1) { 185 len = argp + ARG_MAX - dp; 186 if ((error = copyin(cpp, &sp, sizeof(sp))) != 0) 187 goto bad; 188 if (!sp) 189 break; 190 if ((error = copyinstr((char *)(u_long)sp, dp, 191 len, &len)) != 0) { 192 if (error == ENAMETOOLONG) 193 error = E2BIG; 194 goto bad; 195 } 196 dp += len; 197 cpp++; 198 argc++; 199 } 200 201 envc = 0; 202 /* environment need not be there */ 203 if ((cpp = (netbsd32_charp *)SCARG(uap, envp)) != NULL ) { 204 while (1) { 205 len = argp + ARG_MAX - dp; 206 if ((error = copyin(cpp, &sp, sizeof(sp))) != 0) 207 goto bad; 208 if (!sp) 209 break; 210 if ((error = copyinstr((char *)(u_long)sp, 211 dp, len, &len)) != 0) { 212 if (error == ENAMETOOLONG) 213 error = E2BIG; 214 goto bad; 215 } 216 dp += len; 217 cpp++; 218 envc++; 219 } 220 } 221 222 dp = (char *) ALIGN(dp); 223 224 szsigcode = pack.ep_es->es_emul->e_esigcode - 225 pack.ep_es->es_emul->e_sigcode; 226 227 /* Now check if args & environ fit into new stack */ 228 if (pack.ep_flags & EXEC_32) 229 len = ((argc + envc + 2 + pack.ep_es->es_arglen) * 230 sizeof(int) + sizeof(int) + dp + STACKGAPLEN + 231 szsigcode + sizeof(struct ps_strings)) - argp; 232 else 233 len = ((argc + envc + 2 + pack.ep_es->es_arglen) * 234 sizeof(char *) + sizeof(int) + dp + STACKGAPLEN + 235 szsigcode + sizeof(struct ps_strings)) - argp; 236 237 len = ALIGN(len); /* make the stack "safely" aligned */ 238 239 if (len > pack.ep_ssize) { /* in effect, compare to initial limit */ 240 error = ENOMEM; 241 goto bad; 242 } 243 244 /* adjust "active stack depth" for process VSZ */ 245 pack.ep_ssize = len; /* maybe should go elsewhere, but... */ 246 247 /* 248 * Do whatever is necessary to prepare the address space 249 * for remapping. Note that this might replace the current 250 * vmspace with another! 251 */ 252 uvmspace_exec(p, VM_MIN_ADDRESS, (vaddr_t)pack.ep_minsaddr); 253 254 /* Now map address space */ 255 vm = p->p_vmspace; 256 vm->vm_taddr = (char *) pack.ep_taddr; 257 vm->vm_tsize = btoc(pack.ep_tsize); 258 vm->vm_daddr = (char *) pack.ep_daddr; 259 vm->vm_dsize = btoc(pack.ep_dsize); 260 vm->vm_ssize = btoc(pack.ep_ssize); 261 vm->vm_maxsaddr = (char *) pack.ep_maxsaddr; 262 vm->vm_minsaddr = (char *) pack.ep_minsaddr; 263 264 /* create the new process's VM space by running the vmcmds */ 265#ifdef DIAGNOSTIC 266 if (pack.ep_vmcmds.evs_used == 0) 267 panic("netbsd32_execve: no vmcmds"); 268#endif 269 for (i = 0; i < pack.ep_vmcmds.evs_used && !error; i++) { 270 struct exec_vmcmd *vcp; 271 272 vcp = &pack.ep_vmcmds.evs_cmds[i]; 273 if (vcp->ev_flags & VMCMD_RELATIVE) { 274#ifdef DIAGNOSTIC 275 if (base_vcp == NULL) 276 panic("netbsd32_execve: relative vmcmd with no base"); 277 if (vcp->ev_flags & VMCMD_BASE) 278 panic("netbsd32_execve: illegal base & relative vmcmd"); 279#endif 280 vcp->ev_addr += base_vcp->ev_addr; 281 } 282 error = (*vcp->ev_proc)(p, vcp); 283#ifdef DEBUG 284 if (error) { 285 int j; 286 287 for (j = 0; j <= i; j++) 288 printf("vmcmd[%d] = %#lx/%#lx @ %#lx\n", j, 289 vcp[j-i].ev_addr, vcp[j-i].ev_len, 290 vcp[j-i].ev_offset); 291 } 292#endif 293 if (vcp->ev_flags & VMCMD_BASE) 294 base_vcp = vcp; 295 } 296 297 /* free the vmspace-creation commands, and release their references */ 298 kill_vmcmds(&pack.ep_vmcmds); 299 300 /* if an error happened, deallocate and punt */ 301 if (error) { 302#ifdef DEBUG 303 printf("netbsd32_execve: vmcmd %i failed: %d\n", i-1, error); 304#endif 305 goto exec_abort; 306 } 307 308 /* remember information about the process */ 309 arginfo.ps_nargvstr = argc; 310 arginfo.ps_nenvstr = envc; 311 312 stack = (char *) (vm->vm_minsaddr - len); 313 /* Now copy argc, args & environ to new stack */ 314 error = (*pack.ep_es->es_copyargs)(&pack, &arginfo, 315 &stack, argp); 316 if (error) { 317#ifdef DEBUG 318 printf("netbsd32_execve: copyargs failed\n"); 319#endif 320 goto exec_abort; 321 } 322 /* restore the stack back to its original point */ 323 stack = (char *) (vm->vm_minsaddr - len); 324 325 /* fill process ps_strings info */ 326 p->p_psstr = (struct ps_strings *)(stack - sizeof(struct ps_strings)); 327 p->p_psargv = offsetof(struct ps_strings, ps_argvstr); 328 p->p_psnargv = offsetof(struct ps_strings, ps_nargvstr); 329 p->p_psenv = offsetof(struct ps_strings, ps_envstr); 330 p->p_psnenv = offsetof(struct ps_strings, ps_nenvstr); 331 332 /* copy out the process's ps_strings structure */ 333 if (copyout(&arginfo, (char *)p->p_psstr, sizeof(arginfo))) { 334#ifdef DEBUG 335 printf("netbsd32_execve: ps_strings copyout failed\n"); 336#endif 337 goto exec_abort; 338 } 339 340 /* copy out the process's signal trapoline code */ 341 if (szsigcode) { 342 if (copyout((char *)pack.ep_es->es_emul->e_sigcode, 343 p->p_sigctx.ps_sigcode = (char *)p->p_psstr - szsigcode, 344 szsigcode)) { 345#ifdef DEBUG 346 printf("netbsd32_execve: sig trampoline copyout failed\n"); 347#endif 348 goto exec_abort; 349 } 350#ifdef PMAP_NEED_PROCWR 351 /* This is code. Let the pmap do what is needed. */ 352 pmap_procwr(p, (vaddr_t)p->p_sigacts->ps_sigcode, szsigcode); 353#endif 354 } 355 356 stopprofclock(p); /* stop profiling */ 357 fdcloseexec(p); /* handle close on exec */ 358 execsigs(p); /* reset catched signals */ 359 p->p_ctxlink = NULL; /* reset ucontext link */ 360 361 /* set command name & other accounting info */ 362 len = min(nid.ni_cnd.cn_namelen, MAXCOMLEN); 363 memcpy(p->p_comm, nid.ni_cnd.cn_nameptr, len); 364 p->p_comm[len] = 0; 365 p->p_acflag &= ~AFORK; 366 367 /* record proc's vnode, for use by procfs and others */ 368 if (p->p_textvp) 369 vrele(p->p_textvp); 370 VREF(pack.ep_vp); 371 p->p_textvp = pack.ep_vp; 372 373 p->p_flag |= P_EXEC; 374 if (p->p_flag & P_PPWAIT) { 375 p->p_flag &= ~P_PPWAIT; 376 wakeup((caddr_t) p->p_pptr); 377 } 378 379 /* 380 * deal with set[ug]id. 381 * MNT_NOSUID has already been used to disable s[ug]id. 382 */ 383 if ((p->p_flag & P_TRACED) == 0 && 384 385 (((attr.va_mode & S_ISUID) != 0 && 386 p->p_ucred->cr_uid != attr.va_uid) || 387 388 ((attr.va_mode & S_ISGID) != 0 && 389 p->p_ucred->cr_gid != attr.va_gid))) { 390 /* 391 * Mark the process as SUGID before we do 392 * anything that might block. 393 */ 394 p_sugid(p); 395 396 p->p_ucred = crcopy(cred); 397#ifdef KTRACE 398 /* 399 * If process is being ktraced, turn off - unless 400 * root set it. 401 */ 402 if (p->p_tracep && !(p->p_traceflag & KTRFAC_ROOT)) 403 ktrderef(p); 404#endif 405 if (attr.va_mode & S_ISUID) 406 p->p_ucred->cr_uid = attr.va_uid; 407 if (attr.va_mode & S_ISGID) 408 p->p_ucred->cr_gid = attr.va_gid; 409 } else 410 p->p_flag &= ~P_SUGID; 411 p->p_cred->p_svuid = p->p_ucred->cr_uid; 412 p->p_cred->p_svgid = p->p_ucred->cr_gid; 413 414 doexechooks(p); 415 416 uvm_km_free_wakeup(exec_map, (vaddr_t) argp, NCARGS); 417 418 PNBUF_PUT(nid.ni_cnd.cn_pnbuf); 419 vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); 420 VOP_CLOSE(pack.ep_vp, FREAD, cred, p); 421 vput(pack.ep_vp); 422 423 /* setup new registers and do misc. setup. */ 424 (*pack.ep_es->es_setregs)(p, &pack, (u_long) stack); 425 426 if (p->p_flag & P_TRACED) 427 psignal(p, SIGTRAP); 428 429 free(pack.ep_hdr, M_EXEC); 430 431 /* 432 * Call emulation specific exec hook. This can setup setup per-process 433 * p->p_emuldata or do any other per-process stuff an emulation needs. 434 * 435 * If we are executing process of different emulation than the 436 * original forked process, call e_proc_exit() of the old emulation 437 * first, then e_proc_exec() of new emulation. If the emulation is 438 * same, the exec hook code should deallocate any old emulation 439 * resources held previously by this process. 440 */ 441 if (p->p_emul && p->p_emul->e_proc_exit 442 && p->p_emul != pack.ep_es->es_emul) 443 (*p->p_emul->e_proc_exit)(p); 444 445 /* 446 * Call exec hook. Emulation code may NOT store reference to anything 447 * from &pack. 448 */ 449 if (pack.ep_es->es_emul->e_proc_exec) 450 (*pack.ep_es->es_emul->e_proc_exec)(p, &pack); 451 452 /* update p_emul, the old value is no longer needed */ 453 p->p_emul = pack.ep_es->es_emul; 454 455#ifdef KTRACE 456 if (KTRPOINT(p, KTR_EMUL)) 457 ktremul(p); 458#endif 459 460 lockmgr(&exec_lock, LK_RELEASE, NULL); 461 462 return (EJUSTRETURN); 463 464bad: 465 /* free the vmspace-creation commands, and release their references */ 466 kill_vmcmds(&pack.ep_vmcmds); 467 /* kill any opened file descriptor, if necessary */ 468 if (pack.ep_flags & EXEC_HASFD) { 469 pack.ep_flags &= ~EXEC_HASFD; 470 (void) fdrelease(p, pack.ep_fd); 471 } 472 /* close and put the exec'd file */ 473 vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); 474 VOP_CLOSE(pack.ep_vp, FREAD, cred, p); 475 vput(pack.ep_vp); 476 PNBUF_PUT(nid.ni_cnd.cn_pnbuf); 477 uvm_km_free_wakeup(exec_map, (vaddr_t) argp, NCARGS); 478 479freehdr: 480 lockmgr(&exec_lock, LK_RELEASE, NULL); 481 482 free(pack.ep_hdr, M_EXEC); 483 return error; 484 485exec_abort: 486 lockmgr(&exec_lock, LK_RELEASE, NULL); 487 488 /* 489 * the old process doesn't exist anymore. exit gracefully. 490 * get rid of the (new) address space we have created, if any, get rid 491 * of our namei data and vnode, and exit noting failure 492 */ 493 uvm_deallocate(&vm->vm_map, VM_MIN_ADDRESS, 494 VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS); 495 if (pack.ep_emul_arg) 496 FREE(pack.ep_emul_arg, M_TEMP); 497 PNBUF_PUT(nid.ni_cnd.cn_pnbuf); 498 vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); 499 VOP_CLOSE(pack.ep_vp, FREAD, cred, p); 500 vput(pack.ep_vp); 501 uvm_km_free_wakeup(exec_map, (vaddr_t) argp, NCARGS); 502 free(pack.ep_hdr, M_EXEC); 503 exit1(p, W_EXITCODE(error, SIGABRT)); 504 505 /* NOTREACHED */ 506 return 0; 507} 508