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
39extern 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
64typedef char const * bear_env_t[ENV_SIZE];
65
66static int bear_capture_env_t(bear_env_t *env);
67static int bear_reset_env_t(bear_env_t *env);
68static void bear_release_env_t(bear_env_t *env);
69static char const **bear_update_environment(char *const envp[], bear_env_t *env);
70static char const **bear_update_environ(char const **in, char const *key, char const *value);
71static char **bear_get_environment();
72static void bear_report_call(char const *fun, char const *const argv[]);
73static char const **bear_strings_build(char const *arg, va_list *ap);
74static char const **bear_strings_copy(char const **const in);
75static char const **bear_strings_append(char const **in, char const *e);
76static size_t bear_strings_length(char const *const *in);
77static void bear_strings_release(char const **);
78
79
80static bear_env_t env_names =
81    { ENV_OUTPUT
82    , ENV_PRELOAD
83#ifdef ENV_FLAT
84    , ENV_FLAT
85#endif
86    };
87
88static bear_env_t initial_env =
89    { 0
90    , 0
91#ifdef ENV_FLAT
92    , 0
93#endif
94    };
95
96static int initialized = 0;
97static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
98
99static void on_load(void) __attribute__((constructor));
100static void on_unload(void) __attribute__((destructor));
101
102
103#ifdef HAVE_EXECVE
104static int call_execve(const char *path, char *const argv[],
105                       char *const envp[]);
106#endif
107#ifdef HAVE_EXECVP
108static int call_execvp(const char *file, char *const argv[]);
109#endif
110#ifdef HAVE_EXECVPE
111static int call_execvpe(const char *file, char *const argv[],
112                        char *const envp[]);
113#endif
114#ifdef HAVE_EXECVP2
115static int call_execvP(const char *file, const char *search_path,
116                       char *const argv[]);
117#endif
118#ifdef HAVE_EXECT
119static int call_exect(const char *path, char *const argv[],
120                      char *const envp[]);
121#endif
122#ifdef HAVE_POSIX_SPAWN
123static 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
130static 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
141static 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
148static 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
160int 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
170int 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
178int 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
185int 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
192int 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
199int 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
209int 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
228int 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[]);
247int 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
264int 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
274int 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
287static 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
301static 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
315static 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
332static 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
350static 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
364static 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
385static 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
407static 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
453static 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
464static 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
476static 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
483static 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
490static 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
520static 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
531static 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
557static 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
579static 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
592static 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
599static 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