Home | History | Annotate | Download | only in libear
      1 /* -*- coding: utf-8 -*-
      2 //                     The LLVM Compiler Infrastructure
      3 //
      4 // This file is distributed under the University of Illinois Open Source
      5 // License. See LICENSE.TXT for details.
      6 */
      7 
      8 /**
      9  * This file implements a shared library. This library can be pre-loaded by
     10  * the dynamic linker of the Operating System (OS). It implements a few function
     11  * related to process creation. By pre-load this library the executed process
     12  * uses these functions instead of those from the standard library.
     13  *
     14  * The idea here is to inject a logic before call the real methods. The logic is
     15  * to dump the call into a file. To call the real method this library is doing
     16  * the job of the dynamic linker.
     17  *
     18  * The only input for the log writing is about the destination directory.
     19  * This is passed as environment variable.
     20  */
     21 
     22 #include "config.h"
     23 
     24 #include <stddef.h>
     25 #include <stdarg.h>
     26 #include <stdlib.h>
     27 #include <stdio.h>
     28 #include <string.h>
     29 #include <unistd.h>
     30 #include <dlfcn.h>
     31 #include <pthread.h>
     32 
     33 #if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP
     34 #include <spawn.h>
     35 #endif
     36 
     37 #if defined HAVE_NSGETENVIRON
     38 # include <crt_externs.h>
     39 #else
     40 extern char **environ;
     41 #endif
     42 
     43 #define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR"
     44 #ifdef APPLE
     45 # define ENV_FLAT    "DYLD_FORCE_FLAT_NAMESPACE"
     46 # define ENV_PRELOAD "DYLD_INSERT_LIBRARIES"
     47 # define ENV_SIZE 3
     48 #else
     49 # define ENV_PRELOAD "LD_PRELOAD"
     50 # define ENV_SIZE 2
     51 #endif
     52 
     53 #define DLSYM(TYPE_, VAR_, SYMBOL_)                                            \
     54     union {                                                                    \
     55         void *from;                                                            \
     56         TYPE_ to;                                                              \
     57     } cast;                                                                    \
     58     if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) {                        \
     59         perror("bear: dlsym");                                                 \
     60         exit(EXIT_FAILURE);                                                    \
     61     }                                                                          \
     62     TYPE_ const VAR_ = cast.to;
     63 
     64 
     65 typedef char const * bear_env_t[ENV_SIZE];
     66 
     67 static int bear_capture_env_t(bear_env_t *env);
     68 static int bear_reset_env_t(bear_env_t *env);
     69 static void bear_release_env_t(bear_env_t *env);
     70 static char const **bear_update_environment(char *const envp[], bear_env_t *env);
     71 static char const **bear_update_environ(char const **in, char const *key, char const *value);
     72 static char **bear_get_environment();
     73 static void bear_report_call(char const *fun, char const *const argv[]);
     74 static char const **bear_strings_build(char const *arg, va_list *ap);
     75 static char const **bear_strings_copy(char const **const in);
     76 static char const **bear_strings_append(char const **in, char const *e);
     77 static size_t bear_strings_length(char const *const *in);
     78 static void bear_strings_release(char const **);
     79 
     80 
     81 static bear_env_t env_names =
     82     { ENV_OUTPUT
     83     , ENV_PRELOAD
     84 #ifdef ENV_FLAT
     85     , ENV_FLAT
     86 #endif
     87     };
     88 
     89 static bear_env_t initial_env =
     90     { 0
     91     , 0
     92 #ifdef ENV_FLAT
     93     , 0
     94 #endif
     95     };
     96 
     97 static int initialized = 0;
     98 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
     99 
    100 static void on_load(void) __attribute__((constructor));
    101 static void on_unload(void) __attribute__((destructor));
    102 
    103 
    104 #ifdef HAVE_EXECVE
    105 static int call_execve(const char *path, char *const argv[],
    106                        char *const envp[]);
    107 #endif
    108 #ifdef HAVE_EXECVP
    109 static int call_execvp(const char *file, char *const argv[]);
    110 #endif
    111 #ifdef HAVE_EXECVPE
    112 static int call_execvpe(const char *file, char *const argv[],
    113                         char *const envp[]);
    114 #endif
    115 #ifdef HAVE_EXECVP2
    116 static int call_execvP(const char *file, const char *search_path,
    117                        char *const argv[]);
    118 #endif
    119 #ifdef HAVE_EXECT
    120 static int call_exect(const char *path, char *const argv[],
    121                       char *const envp[]);
    122 #endif
    123 #ifdef HAVE_POSIX_SPAWN
    124 static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
    125                             const posix_spawn_file_actions_t *file_actions,
    126                             const posix_spawnattr_t *restrict attrp,
    127                             char *const argv[restrict],
    128                             char *const envp[restrict]);
    129 #endif
    130 #ifdef HAVE_POSIX_SPAWNP
    131 static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
    132                              const posix_spawn_file_actions_t *file_actions,
    133                              const posix_spawnattr_t *restrict attrp,
    134                              char *const argv[restrict],
    135                              char *const envp[restrict]);
    136 #endif
    137 
    138 
    139 /* Initialization method to Captures the relevant environment variables.
    140  */
    141 
    142 static void on_load(void) {
    143     pthread_mutex_lock(&mutex);
    144     if (!initialized)
    145         initialized = bear_capture_env_t(&initial_env);
    146     pthread_mutex_unlock(&mutex);
    147 }
    148 
    149 static void on_unload(void) {
    150     pthread_mutex_lock(&mutex);
    151     bear_release_env_t(&initial_env);
    152     initialized = 0;
    153     pthread_mutex_unlock(&mutex);
    154 }
    155 
    156 
    157 /* These are the methods we are try to hijack.
    158  */
    159 
    160 #ifdef HAVE_EXECVE
    161 int execve(const char *path, char *const argv[], char *const envp[]) {
    162     bear_report_call(__func__, (char const *const *)argv);
    163     return call_execve(path, argv, envp);
    164 }
    165 #endif
    166 
    167 #ifdef HAVE_EXECV
    168 #ifndef HAVE_EXECVE
    169 #error can not implement execv without execve
    170 #endif
    171 int execv(const char *path, char *const argv[]) {
    172     bear_report_call(__func__, (char const *const *)argv);
    173     char * const * envp = bear_get_environment();
    174     return call_execve(path, argv, envp);
    175 }
    176 #endif
    177 
    178 #ifdef HAVE_EXECVPE
    179 int execvpe(const char *file, char *const argv[], char *const envp[]) {
    180     bear_report_call(__func__, (char const *const *)argv);
    181     return call_execvpe(file, argv, envp);
    182 }
    183 #endif
    184 
    185 #ifdef HAVE_EXECVP
    186 int execvp(const char *file, char *const argv[]) {
    187     bear_report_call(__func__, (char const *const *)argv);
    188     return call_execvp(file, argv);
    189 }
    190 #endif
    191 
    192 #ifdef HAVE_EXECVP2
    193 int execvP(const char *file, const char *search_path, char *const argv[]) {
    194     bear_report_call(__func__, (char const *const *)argv);
    195     return call_execvP(file, search_path, argv);
    196 }
    197 #endif
    198 
    199 #ifdef HAVE_EXECT
    200 int exect(const char *path, char *const argv[], char *const envp[]) {
    201     bear_report_call(__func__, (char const *const *)argv);
    202     return call_exect(path, argv, envp);
    203 }
    204 #endif
    205 
    206 #ifdef HAVE_EXECL
    207 # ifndef HAVE_EXECVE
    208 #  error can not implement execl without execve
    209 # endif
    210 int execl(const char *path, const char *arg, ...) {
    211     va_list args;
    212     va_start(args, arg);
    213     char const **argv = bear_strings_build(arg, &args);
    214     va_end(args);
    215 
    216     bear_report_call(__func__, (char const *const *)argv);
    217     char * const * envp = bear_get_environment();
    218     int const result = call_execve(path, (char *const *)argv, envp);
    219 
    220     bear_strings_release(argv);
    221     return result;
    222 }
    223 #endif
    224 
    225 #ifdef HAVE_EXECLP
    226 # ifndef HAVE_EXECVP
    227 #  error can not implement execlp without execvp
    228 # endif
    229 int execlp(const char *file, const char *arg, ...) {
    230     va_list args;
    231     va_start(args, arg);
    232     char const **argv = bear_strings_build(arg, &args);
    233     va_end(args);
    234 
    235     bear_report_call(__func__, (char const *const *)argv);
    236     int const result = call_execvp(file, (char *const *)argv);
    237 
    238     bear_strings_release(argv);
    239     return result;
    240 }
    241 #endif
    242 
    243 #ifdef HAVE_EXECLE
    244 # ifndef HAVE_EXECVE
    245 #  error can not implement execle without execve
    246 # endif
    247 // int execle(const char *path, const char *arg, ..., char * const envp[]);
    248 int execle(const char *path, const char *arg, ...) {
    249     va_list args;
    250     va_start(args, arg);
    251     char const **argv = bear_strings_build(arg, &args);
    252     char const **envp = va_arg(args, char const **);
    253     va_end(args);
    254 
    255     bear_report_call(__func__, (char const *const *)argv);
    256     int const result =
    257         call_execve(path, (char *const *)argv, (char *const *)envp);
    258 
    259     bear_strings_release(argv);
    260     return result;
    261 }
    262 #endif
    263 
    264 #ifdef HAVE_POSIX_SPAWN
    265 int posix_spawn(pid_t *restrict pid, const char *restrict path,
    266                 const posix_spawn_file_actions_t *file_actions,
    267                 const posix_spawnattr_t *restrict attrp,
    268                 char *const argv[restrict], char *const envp[restrict]) {
    269     bear_report_call(__func__, (char const *const *)argv);
    270     return call_posix_spawn(pid, path, file_actions, attrp, argv, envp);
    271 }
    272 #endif
    273 
    274 #ifdef HAVE_POSIX_SPAWNP
    275 int posix_spawnp(pid_t *restrict pid, const char *restrict file,
    276                  const posix_spawn_file_actions_t *file_actions,
    277                  const posix_spawnattr_t *restrict attrp,
    278                  char *const argv[restrict], char *const envp[restrict]) {
    279     bear_report_call(__func__, (char const *const *)argv);
    280     return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp);
    281 }
    282 #endif
    283 
    284 /* These are the methods which forward the call to the standard implementation.
    285  */
    286 
    287 #ifdef HAVE_EXECVE
    288 static int call_execve(const char *path, char *const argv[],
    289                        char *const envp[]) {
    290     typedef int (*func)(const char *, char *const *, char *const *);
    291 
    292     DLSYM(func, fp, "execve");
    293 
    294     char const **const menvp = bear_update_environment(envp, &initial_env);
    295     int const result = (*fp)(path, argv, (char *const *)menvp);
    296     bear_strings_release(menvp);
    297     return result;
    298 }
    299 #endif
    300 
    301 #ifdef HAVE_EXECVPE
    302 static int call_execvpe(const char *file, char *const argv[],
    303                         char *const envp[]) {
    304     typedef int (*func)(const char *, char *const *, char *const *);
    305 
    306     DLSYM(func, fp, "execvpe");
    307 
    308     char const **const menvp = bear_update_environment(envp, &initial_env);
    309     int const result = (*fp)(file, argv, (char *const *)menvp);
    310     bear_strings_release(menvp);
    311     return result;
    312 }
    313 #endif
    314 
    315 #ifdef HAVE_EXECVP
    316 static int call_execvp(const char *file, char *const argv[]) {
    317     typedef int (*func)(const char *file, char *const argv[]);
    318 
    319     DLSYM(func, fp, "execvp");
    320 
    321     bear_env_t current_env;
    322     bear_capture_env_t(&current_env);
    323     bear_reset_env_t(&initial_env);
    324     int const result = (*fp)(file, argv);
    325     bear_reset_env_t(&current_env);
    326     bear_release_env_t(&current_env);
    327 
    328     return result;
    329 }
    330 #endif
    331 
    332 #ifdef HAVE_EXECVP2
    333 static int call_execvP(const char *file, const char *search_path,
    334                        char *const argv[]) {
    335     typedef int (*func)(const char *, const char *, char *const *);
    336 
    337     DLSYM(func, fp, "execvP");
    338 
    339     bear_env_t current_env;
    340     bear_capture_env_t(&current_env);
    341     bear_reset_env_t(&initial_env);
    342     int const result = (*fp)(file, search_path, argv);
    343     bear_reset_env_t(&current_env);
    344     bear_release_env_t(&current_env);
    345 
    346     return result;
    347 }
    348 #endif
    349 
    350 #ifdef HAVE_EXECT
    351 static int call_exect(const char *path, char *const argv[],
    352                       char *const envp[]) {
    353     typedef int (*func)(const char *, char *const *, char *const *);
    354 
    355     DLSYM(func, fp, "exect");
    356 
    357     char const **const menvp = bear_update_environment(envp, &initial_env);
    358     int const result = (*fp)(path, argv, (char *const *)menvp);
    359     bear_strings_release(menvp);
    360     return result;
    361 }
    362 #endif
    363 
    364 #ifdef HAVE_POSIX_SPAWN
    365 static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
    366                             const posix_spawn_file_actions_t *file_actions,
    367                             const posix_spawnattr_t *restrict attrp,
    368                             char *const argv[restrict],
    369                             char *const envp[restrict]) {
    370     typedef int (*func)(pid_t *restrict, const char *restrict,
    371                         const posix_spawn_file_actions_t *,
    372                         const posix_spawnattr_t *restrict,
    373                         char *const *restrict, char *const *restrict);
    374 
    375     DLSYM(func, fp, "posix_spawn");
    376 
    377     char const **const menvp = bear_update_environment(envp, &initial_env);
    378     int const result =
    379         (*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp);
    380     bear_strings_release(menvp);
    381     return result;
    382 }
    383 #endif
    384 
    385 #ifdef HAVE_POSIX_SPAWNP
    386 static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
    387                              const posix_spawn_file_actions_t *file_actions,
    388                              const posix_spawnattr_t *restrict attrp,
    389                              char *const argv[restrict],
    390                              char *const envp[restrict]) {
    391     typedef int (*func)(pid_t *restrict, const char *restrict,
    392                         const posix_spawn_file_actions_t *,
    393                         const posix_spawnattr_t *restrict,
    394                         char *const *restrict, char *const *restrict);
    395 
    396     DLSYM(func, fp, "posix_spawnp");
    397 
    398     char const **const menvp = bear_update_environment(envp, &initial_env);
    399     int const result =
    400         (*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp);
    401     bear_strings_release(menvp);
    402     return result;
    403 }
    404 #endif
    405 
    406 /* this method is to write log about the process creation. */
    407 
    408 static void bear_report_call(char const *fun, char const *const argv[]) {
    409     static int const GS = 0x1d;
    410     static int const RS = 0x1e;
    411     static int const US = 0x1f;
    412 
    413     if (!initialized)
    414         return;
    415 
    416     pthread_mutex_lock(&mutex);
    417     const char *cwd = getcwd(NULL, 0);
    418     if (0 == cwd) {
    419         perror("bear: getcwd");
    420         exit(EXIT_FAILURE);
    421     }
    422     char const * const out_dir = initial_env[0];
    423     size_t const path_max_length = strlen(out_dir) + 32;
    424     char filename[path_max_length];
    425     if (-1 == snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) {
    426         perror("bear: snprintf");
    427         exit(EXIT_FAILURE);
    428     }
    429     FILE * fd = fopen(filename, "a+");
    430     if (0 == fd) {
    431         perror("bear: fopen");
    432         exit(EXIT_FAILURE);
    433     }
    434     fprintf(fd, "%d%c", getpid(), RS);
    435     fprintf(fd, "%d%c", getppid(), RS);
    436     fprintf(fd, "%s%c", fun, RS);
    437     fprintf(fd, "%s%c", cwd, RS);
    438     size_t const argc = bear_strings_length(argv);
    439     for (size_t it = 0; it < argc; ++it) {
    440         fprintf(fd, "%s%c", argv[it], US);
    441     }
    442     fprintf(fd, "%c", GS);
    443     if (fclose(fd)) {
    444         perror("bear: fclose");
    445         exit(EXIT_FAILURE);
    446     }
    447     free((void *)cwd);
    448     pthread_mutex_unlock(&mutex);
    449 }
    450 
    451 /* update environment assure that chilren processes will copy the desired
    452  * behaviour */
    453 
    454 static int bear_capture_env_t(bear_env_t *env) {
    455     int status = 1;
    456     for (size_t it = 0; it < ENV_SIZE; ++it) {
    457         char const * const env_value = getenv(env_names[it]);
    458         char const * const env_copy = (env_value) ? strdup(env_value) : env_value;
    459         (*env)[it] = env_copy;
    460         status &= (env_copy) ? 1 : 0;
    461     }
    462     return status;
    463 }
    464 
    465 static int bear_reset_env_t(bear_env_t *env) {
    466     int status = 1;
    467     for (size_t it = 0; it < ENV_SIZE; ++it) {
    468         if ((*env)[it]) {
    469             setenv(env_names[it], (*env)[it], 1);
    470         } else {
    471             unsetenv(env_names[it]);
    472         }
    473     }
    474     return status;
    475 }
    476 
    477 static void bear_release_env_t(bear_env_t *env) {
    478     for (size_t it = 0; it < ENV_SIZE; ++it) {
    479         free((void *)(*env)[it]);
    480         (*env)[it] = 0;
    481     }
    482 }
    483 
    484 static char const **bear_update_environment(char *const envp[], bear_env_t *env) {
    485     char const **result = bear_strings_copy((char const **)envp);
    486     for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it)
    487         result = bear_update_environ(result, env_names[it], (*env)[it]);
    488     return result;
    489 }
    490 
    491 static char const **bear_update_environ(char const *envs[], char const *key, char const * const value) {
    492     // find the key if it's there
    493     size_t const key_length = strlen(key);
    494     char const **it = envs;
    495     for (; (it) && (*it); ++it) {
    496         if ((0 == strncmp(*it, key, key_length)) &&
    497             (strlen(*it) > key_length) && ('=' == (*it)[key_length]))
    498             break;
    499     }
    500     // allocate a environment entry
    501     size_t const value_length = strlen(value);
    502     size_t const env_length = key_length + value_length + 2;
    503     char *env = malloc(env_length);
    504     if (0 == env) {
    505         perror("bear: malloc [in env_update]");
    506         exit(EXIT_FAILURE);
    507     }
    508     if (-1 == snprintf(env, env_length, "%s=%s", key, value)) {
    509         perror("bear: snprintf");
    510         exit(EXIT_FAILURE);
    511     }
    512     // replace or append the environment entry
    513     if (it && *it) {
    514         free((void *)*it);
    515         *it = env;
    516 	return envs;
    517     }
    518     return bear_strings_append(envs, env);
    519 }
    520 
    521 static char **bear_get_environment() {
    522 #if defined HAVE_NSGETENVIRON
    523     return *_NSGetEnviron();
    524 #else
    525     return environ;
    526 #endif
    527 }
    528 
    529 /* util methods to deal with string arrays. environment and process arguments
    530  * are both represented as string arrays. */
    531 
    532 static char const **bear_strings_build(char const *const arg, va_list *args) {
    533     char const **result = 0;
    534     size_t size = 0;
    535     for (char const *it = arg; it; it = va_arg(*args, char const *)) {
    536         result = realloc(result, (size + 1) * sizeof(char const *));
    537         if (0 == result) {
    538             perror("bear: realloc");
    539             exit(EXIT_FAILURE);
    540         }
    541         char const *copy = strdup(it);
    542         if (0 == copy) {
    543             perror("bear: strdup");
    544             exit(EXIT_FAILURE);
    545         }
    546         result[size++] = copy;
    547     }
    548     result = realloc(result, (size + 1) * sizeof(char const *));
    549     if (0 == result) {
    550         perror("bear: realloc");
    551         exit(EXIT_FAILURE);
    552     }
    553     result[size++] = 0;
    554 
    555     return result;
    556 }
    557 
    558 static char const **bear_strings_copy(char const **const in) {
    559     size_t const size = bear_strings_length(in);
    560 
    561     char const **const result = malloc((size + 1) * sizeof(char const *));
    562     if (0 == result) {
    563         perror("bear: malloc");
    564         exit(EXIT_FAILURE);
    565     }
    566 
    567     char const **out_it = result;
    568     for (char const *const *in_it = in; (in_it) && (*in_it);
    569          ++in_it, ++out_it) {
    570         *out_it = strdup(*in_it);
    571         if (0 == *out_it) {
    572             perror("bear: strdup");
    573             exit(EXIT_FAILURE);
    574         }
    575     }
    576     *out_it = 0;
    577     return result;
    578 }
    579 
    580 static char const **bear_strings_append(char const **const in,
    581                                         char const *const e) {
    582     size_t size = bear_strings_length(in);
    583     char const **result = realloc(in, (size + 2) * sizeof(char const *));
    584     if (0 == result) {
    585         perror("bear: realloc");
    586         exit(EXIT_FAILURE);
    587     }
    588     result[size++] = e;
    589     result[size++] = 0;
    590     return result;
    591 }
    592 
    593 static size_t bear_strings_length(char const *const *const in) {
    594     size_t result = 0;
    595     for (char const *const *it = in; (it) && (*it); ++it)
    596         ++result;
    597     return result;
    598 }
    599 
    600 static void bear_strings_release(char const **in) {
    601     for (char const *const *it = in; (it) && (*it); ++it) {
    602         free((void *)*it);
    603     }
    604     free((void *)in);
    605 }
    606