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