Prex Home / Browse Source - Prex Version: 0.9.0

root/usr/server/exec/exec_execve.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. exec_execve
  2. conv_path
  3. build_args
  4. notify_server
  5. read_header

   1 /*-
   2  * Copyright (c) 2005-2009, Kohsuke Ohtani
   3  * All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 1. Redistributions of source code must retain the above copyright
   9  *    notice, this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright
  11  *    notice, this list of conditions and the following disclaimer in the
  12  *    documentation and/or other materials provided with the distribution.
  13  * 3. Neither the name of the author nor the names of any co-contributors
  14  *    may be used to endorse or promote products derived from this software
  15  *    without specific prior written permission.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  */
  29 
  30 /*
  31  * exec_execve.c - execve support
  32  */
  33 
  34 #include <sys/prex.h>
  35 #include <sys/capability.h>
  36 #include <ipc/fs.h>
  37 #include <ipc/proc.h>
  38 #include <ipc/ipc.h>
  39 #include <sys/list.h>
  40 
  41 #include <limits.h>
  42 #include <unistd.h>
  43 #include <stdlib.h>
  44 #include <string.h>
  45 #include <stdio.h>
  46 #include <fcntl.h>
  47 #include <unistd.h>
  48 #include <assert.h>
  49 #include <errno.h>
  50 #include <libgen.h>     /* for basename() */
  51 
  52 #include "exec.h"
  53 
  54 #define SP_ALIGN(p)     ((unsigned)(p) &~ _ALIGNBYTES)
  55 
  56 /* forward declarations */
  57 static int      build_args(task_t, void *, char *, struct exec_msg *,
  58                            char *, char *, void **);
  59 static int      conv_path(char *, char *, char *);
  60 static void     notify_server(task_t, task_t, void *);
  61 static int      read_header(char *);
  62 
  63 /*
  64  * Buffer for file header
  65  */
  66 static char hdrbuf[HEADER_SIZE];
  67 
  68 /*
  69  * Execute program
  70  */
  71 int
  72 exec_execve(struct exec_msg *msg)
  73 {
  74         struct exec_loader *ldr = NULL;
  75         int error, i;
  76         task_t old_task, new_task;
  77         thread_t t;
  78         void *stack, *sp;
  79         char path[PATH_MAX];
  80         struct exec exec;
  81         int rc;
  82 
  83         DPRINTF(("exec_execve: path=%s task=%x\n", msg->path, msg->hdr.task));
  84 
  85         old_task = msg->hdr.task;
  86 
  87         /*
  88          * Make it full path.
  89          */
  90         if ((error = conv_path(msg->cwd, msg->path, path)) != 0) {
  91                 DPRINTF(("exec: invalid path\n"));
  92                 goto err1;
  93         }
  94 
  95         /*
  96          * Check permission.
  97          */
  98         if (access(path, X_OK) == -1) {
  99                 DPRINTF(("exec: no exec access\n"));
 100                 error = errno;
 101                 goto err1;
 102         }
 103 
 104         exec.path = path;
 105         exec.header = hdrbuf;
 106         exec.xarg1 = NULL;
 107         exec.xarg2 = NULL;
 108 
 109  again:
 110         /*
 111          * Read file header
 112          */
 113         DPRINTF(("exec: read header for %s\n", exec.path));
 114         if ((error = read_header(exec.path)) != 0)
 115                 goto err1;
 116 
 117         /*
 118          * Find file loader
 119          */
 120         rc = PROBE_ERROR;
 121         for (i = 0; i < nloader; i++) {
 122                 ldr = &loader_table[i];
 123                 if ((rc = ldr->el_probe(&exec)) != PROBE_ERROR) {
 124                         break;
 125                 }
 126         }
 127         if (rc == PROBE_ERROR) {
 128                 DPRINTF(("exec: unsupported file format\n"));
 129                 error = ENOEXEC;
 130                 goto err1;
 131         }
 132 
 133         /*
 134          * Check file header again if indirect case.
 135          */
 136         if (rc == PROBE_INDIRECT)
 137                         goto again;
 138 
 139         DPRINTF(("exec: loader=%s\n", ldr->el_name));
 140 
 141         /*
 142          * Check file permission.
 143          */
 144         if (access(exec.path, X_OK) == -1) {
 145                 DPRINTF(("exec: no exec access\n"));
 146                 error = errno;
 147                 goto err1;
 148         }
 149 
 150         /*
 151          * Suspend old task
 152          */
 153         if ((error = task_suspend(old_task)) != 0)
 154                 goto err1;
 155 
 156         /*
 157          * Create new task
 158          */
 159         if ((error = task_create(old_task, VM_NEW, &new_task)) != 0) {
 160                 DPRINTF(("exec: failed to crete task\n"));
 161                 goto err1;
 162         }
 163 
 164         if (*exec.path != '\0')
 165                 task_setname(new_task, basename(exec.path));
 166 
 167         /*
 168          * Bind capabilities.
 169          */
 170         bind_cap(exec.path, new_task);
 171 
 172         if ((error = thread_create(new_task, &t)) != 0)
 173                 goto err3;
 174 
 175         /*
 176          * Allocate stack and build arguments on it.
 177          */
 178         error = vm_allocate(new_task, &stack, DFLSTKSZ, 1);
 179         if (error) {
 180                 DPRINTF(("exec: failed to allocate stack\n"));
 181                 goto err4;
 182         }
 183         if ((error = build_args(new_task, stack, exec.path, msg,
 184                                 exec.xarg1, exec.xarg2, &sp)) != 0)
 185                 goto err5;
 186 
 187         /*
 188          * Load file image.
 189          */
 190         DPRINTF(("exec: load file image\n"));
 191         exec.task = new_task;
 192         if ((error = ldr->el_load(&exec)) != 0)
 193                 goto err5;
 194         if ((error = thread_load(t, (void (*)(void))exec.entry, sp)) != 0)
 195                 goto err5;
 196 
 197         /*
 198          * Notify to servers.
 199          */
 200         notify_server(old_task, new_task, stack);
 201 
 202         /*
 203          * Terminate old task.
 204          */
 205         task_terminate(old_task);
 206 
 207         /*
 208          * Set him running.
 209          */
 210         thread_setpri(t, PRI_DEFAULT);
 211         thread_resume(t);
 212 
 213         DPRINTF(("exec done\n"));
 214         return 0;
 215  err5:
 216         vm_free(new_task, stack);
 217  err4:
 218         thread_terminate(t);
 219  err3:
 220         task_terminate(new_task);
 221  err1:
 222         DPRINTF(("exec failed error=%d\n", error));
 223         return error;
 224 }
 225 
 226 /*
 227  * Convert to full path from the cwd of task and path.
 228  * @cwd:  current working directory
 229  * @path: target path
 230  * @full: full path to be returned
 231  */
 232 static int
 233 conv_path(char *cwd, char *path, char *full)
 234 {
 235         char *src, *tgt, *p, *end;
 236         size_t len = 0;
 237 
 238         path[PATH_MAX - 1] = '\0';
 239         len = strlen(path);
 240         if (len >= PATH_MAX)
 241                 return ENAMETOOLONG;
 242         if (strlen(cwd) + len >= PATH_MAX)
 243                 return ENAMETOOLONG;
 244         src = path;
 245         tgt = full;
 246         end = src + len;
 247         if (path[0] == '/') {
 248                 *tgt++ = *src++;
 249                 len++;
 250         } else {
 251                 strlcpy(full, cwd, PATH_MAX);
 252                 len = strlen(cwd);
 253                 tgt += len;
 254                 if (len > 1 && path[0] != '.') {
 255                         *tgt = '/';
 256                         tgt++;
 257                         len++;
 258                 }
 259         }
 260         while (*src) {
 261                 p = src;
 262                 while (*p != '/' && *p != '\0')
 263                         p++;
 264                 *p = '\0';
 265                 if (!strcmp(src, "..")) {
 266                         if (len >= 2) {
 267                                 len -= 2;
 268                                 tgt -= 2;       /* skip previous '/' */
 269                                 while (*tgt != '/') {
 270                                         tgt--;
 271                                         len--;
 272                                 }
 273                                 if (len == 0) {
 274                                         tgt++;
 275                                         len++;
 276                                 }
 277                         }
 278                 } else if (!strcmp(src, ".")) {
 279                         /* Ignore "." */
 280                 } else {
 281                         while (*src != '\0') {
 282                                 *tgt++ = *src++;
 283                                 len++;
 284                         }
 285                 }
 286                 if (p == end)
 287                         break;
 288                 if (len > 0 && *(tgt - 1) != '/') {
 289                         *tgt++ = '/';
 290                         len++;
 291                 }
 292                 src = p + 1;
 293         }
 294         *tgt = '\0';
 295         return 0;
 296 }
 297 
 298 /*
 299  * Build argument on stack.
 300  *
 301  * Stack layout:
 302  *    file name string
 303  *    env string
 304  *    arg string
 305  *    NULL
 306  *    envp[n]
 307  *    NULL
 308  *    argv[n]
 309  *    argc
 310  *
 311  * NOTE: This may depend on processor architecture.
 312  */
 313 static int
 314 build_args(task_t task, void *stack, char *path, struct exec_msg *msg,
 315            char *xarg1, char *xarg2, void **new_sp)
 316 {
 317         int argc, envc;
 318         char *file;
 319         char **argv, **envp;
 320         int i, error;
 321         u_long arg_top, mapped, sp;
 322         int len;
 323 
 324         argc = msg->argc;
 325         envc = msg->envc;
 326         DPRINTF(("exec: argc=%d envc=%d\n", argc, envc));
 327         DPRINTF(("exec: xarg1=%s xarg2=%s\n", xarg1, xarg2));
 328 
 329         /*
 330          * Map target stack in current task.
 331          */
 332         error = vm_map(task, stack, DFLSTKSZ, (void *)&mapped);
 333         if (error)
 334                 return ENOMEM;
 335         memset((void *)mapped, 0, DFLSTKSZ);
 336         sp = mapped + DFLSTKSZ - sizeof(int) * 3;
 337 
 338         /*
 339          * Copy items
 340          */
 341 
 342         /* File name */
 343         *(char *)sp = '\0';
 344         sp -= strlen(path);
 345         sp = SP_ALIGN(sp);
 346         strlcpy((char *)sp, path, PATH_MAX);
 347         file = (char *)sp;
 348 
 349         /* arg/env */
 350         sp -= msg->bufsz;
 351         sp = SP_ALIGN(sp);
 352         memcpy((char *)sp, (char *)&msg->buf, msg->bufsz);
 353         arg_top = sp;
 354 
 355         /*
 356          * Insert extra argument for indirect loader.
 357          */
 358         if (xarg2 != NULL) {
 359                 len = strlen(xarg2);
 360                 sp -= (len + 1);
 361                 strlcpy((char *)sp, xarg2, len + 1);
 362                 arg_top = sp;
 363                 argc++;
 364         }
 365         if (xarg1 != NULL) {
 366                 len = strlen(xarg1);
 367                 sp -= (len + 1);
 368                 strlcpy((char *)sp, xarg1, len + 1);
 369                 arg_top = sp;
 370                 argc++;
 371         }
 372 
 373         /* envp[] */
 374         sp -= ((envc + 1) * sizeof(char *));
 375         envp = (char **)sp;
 376 
 377         /* argv[] */
 378         sp -= ((argc + 1) * sizeof(char *));
 379         argv = (char **)sp;
 380 
 381         /* argc */
 382         sp -= sizeof(int);
 383         *(int *)(sp) = argc + 1;
 384 
 385         /*
 386          * Build argument list
 387          */
 388         argv[0] = (char *)((u_long)stack + (u_long)file - mapped);
 389 
 390         for (i = 1; i <= argc; i++) {
 391                 argv[i] = (char *)((u_long)stack + (arg_top - mapped));
 392                 while ((*(char *)arg_top++) != '\0');
 393         }
 394         argv[argc + 1] = NULL;
 395 
 396         for (i = 0; i < envc; i++) {
 397                 envp[i] = (char *)((u_long)stack + (arg_top - mapped));
 398                 while ((*(char *)arg_top++) != '\0');
 399         }
 400         envp[envc] = NULL;
 401 
 402         *new_sp = (void *)((u_long)stack + (sp - mapped));
 403         vm_free(task_self(), (void *)mapped);
 404 
 405         return 0;
 406 }
 407 
 408 /*
 409  * Notify exec() to servers.
 410  */
 411 static void
 412 notify_server(task_t org_task, task_t new_task, void *stack)
 413 {
 414         struct msg m;
 415         int error;
 416         object_t fsobj, procobj;
 417 
 418         if (object_lookup("!fs", &fsobj) != 0)
 419                 return;
 420 
 421         if (object_lookup("!proc", &procobj) != 0)
 422                 return;
 423 
 424         /* Notify to file system server */
 425         do {
 426                 m.hdr.code = FS_EXEC;
 427                 m.data[0] = (int)org_task;
 428                 m.data[1] = (int)new_task;
 429                 error = msg_send(fsobj, &m, sizeof(m));
 430         } while (error == EINTR);
 431 
 432         /* Notify to process server */
 433         do {
 434                 m.hdr.code = PS_EXEC;
 435                 m.data[0] = (int)org_task;
 436                 m.data[1] = (int)new_task;
 437                 m.data[2] = (int)stack;
 438                 error = msg_send(procobj, &m, sizeof(m));
 439         } while (error == EINTR);
 440 }
 441 
 442 static int
 443 read_header(char *path)
 444 {
 445         int fd;
 446         struct stat st;
 447 
 448         /*
 449          * Check target file type.
 450          */
 451         if ((fd = open(path, O_RDONLY)) == -1)
 452                 return ENOENT;
 453 
 454         if (fstat(fd, &st) == -1) {
 455                 close(fd);
 456                 return EIO;
 457         }
 458         if (!S_ISREG(st.st_mode)) {
 459                 DPRINTF(("exec: not regular file\n"));
 460                 close(fd);
 461                 return EACCES;  /* must be regular file */
 462         }
 463         /*
 464          * Read file header.
 465          */
 466         memset(hdrbuf, 0, HEADER_SIZE);
 467         if (read(fd, hdrbuf, HEADER_SIZE) == -1) {
 468                 close(fd);
 469                 return EIO;
 470         }
 471         close(fd);
 472         return 0;
 473 }

/* [<][>][^][v][top][bottom][index][help] */