• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
on_load(void)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 
on_unload(void)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
execve(const char * path,char * const argv[],char * const envp[])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
execv(const char * path,char * const argv[])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
execvpe(const char * file,char * const argv[],char * const envp[])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
execvp(const char * file,char * const argv[])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
execvP(const char * file,const char * search_path,char * const argv[])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
exect(const char * path,char * const argv[],char * const envp[])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
execl(const char * path,const char * arg,...)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
execlp(const char * file,const char * arg,...)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[]);
execle(const char * path,const char * arg,...)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
posix_spawn(pid_t * restrict pid,const char * restrict path,const posix_spawn_file_actions_t * file_actions,const posix_spawnattr_t * restrict attrp,char * const argv[restrict],char * const envp[restrict])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
posix_spawnp(pid_t * restrict pid,const char * restrict file,const posix_spawn_file_actions_t * file_actions,const posix_spawnattr_t * restrict attrp,char * const argv[restrict],char * const envp[restrict])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
call_execve(const char * path,char * const argv[],char * const envp[])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
call_execvpe(const char * file,char * const argv[],char * const envp[])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
call_execvp(const char * file,char * const argv[])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
call_execvP(const char * file,const char * search_path,char * const argv[])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
call_exect(const char * path,char * const argv[],char * const envp[])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
call_posix_spawn(pid_t * restrict pid,const char * restrict path,const posix_spawn_file_actions_t * file_actions,const posix_spawnattr_t * restrict attrp,char * const argv[restrict],char * const envp[restrict])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
call_posix_spawnp(pid_t * restrict pid,const char * restrict file,const posix_spawn_file_actions_t * file_actions,const posix_spawnattr_t * restrict attrp,char * const argv[restrict],char * const envp[restrict])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 
bear_report_call(char const * fun,char const * const argv[])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 
bear_capture_env_t(bear_env_t * env)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 
bear_reset_env_t(bear_env_t * env)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 
bear_release_env_t(bear_env_t * env)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 
bear_update_environment(char * const envp[],bear_env_t * env)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 
bear_update_environ(char const * envs[],char const * key,char const * const value)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 
bear_get_environment()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 
bear_strings_build(char const * const arg,va_list * args)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 
bear_strings_copy(char const ** const in)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 
bear_strings_append(char const ** const in,char const * const e)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 
bear_strings_length(char const * const * const in)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 
bear_strings_release(char const ** in)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