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
40extern 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
65typedef char const * bear_env_t[ENV_SIZE];
66
67static int bear_capture_env_t(bear_env_t *env);
68static int bear_reset_env_t(bear_env_t *env);
69static void bear_release_env_t(bear_env_t *env);
70static char const **bear_update_environment(char *const envp[], bear_env_t *env);
71static char const **bear_update_environ(char const **in, char const *key, char const *value);
72static char **bear_get_environment();
73static void bear_report_call(char const *fun, char const *const argv[]);
74static char const **bear_strings_build(char const *arg, va_list *ap);
75static char const **bear_strings_copy(char const **const in);
76static char const **bear_strings_append(char const **in, char const *e);
77static size_t bear_strings_length(char const *const *in);
78static void bear_strings_release(char const **);
79
80
81static bear_env_t env_names =
82    { ENV_OUTPUT
83    , ENV_PRELOAD
84#ifdef ENV_FLAT
85    , ENV_FLAT
86#endif
87    };
88
89static bear_env_t initial_env =
90    { 0
91    , 0
92#ifdef ENV_FLAT
93    , 0
94#endif
95    };
96
97static int initialized = 0;
98static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
99
100static void on_load(void) __attribute__((constructor));
101static void on_unload(void) __attribute__((destructor));
102
103
104#ifdef HAVE_EXECVE
105static int call_execve(const char *path, char *const argv[],
106                       char *const envp[]);
107#endif
108#ifdef HAVE_EXECVP
109static int call_execvp(const char *file, char *const argv[]);
110#endif
111#ifdef HAVE_EXECVPE
112static int call_execvpe(const char *file, char *const argv[],
113                        char *const envp[]);
114#endif
115#ifdef HAVE_EXECVP2
116static int call_execvP(const char *file, const char *search_path,
117                       char *const argv[]);
118#endif
119#ifdef HAVE_EXECT
120static int call_exect(const char *path, char *const argv[],
121                      char *const envp[]);
122#endif
123#ifdef HAVE_POSIX_SPAWN
124static 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
131static 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
142static 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
149static 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
161int 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
171int 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
179int 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
186int 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
193int 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
200int 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
210int 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
229int 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[]);
248int 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
265int 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
275int 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
288static 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
302static 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
316static 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
333static 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
351static 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
365static 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
386static 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
408static 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
454static 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
465static 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
477static 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
484static 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
491static 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
521static 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
532static 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
558static 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
580static 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
593static 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
600static 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