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