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 // NOLINTNEXTLINE
22 #include "config.h"
23 
24 #include <dlfcn.h>
25 #include <pthread.h>
26 #include <stdarg.h>
27 #include <stddef.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.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 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[],
70                                             bear_env_t *env);
71 static char const **bear_update_environ(char const **in, char const *key,
72                                         char const *value);
73 static char **bear_get_environment();
74 static void bear_report_call(char const *fun, char const *const argv[]);
75 static char const **bear_strings_build(char const *arg, va_list *ap);
76 static char const **bear_strings_copy(char const **const in);
77 static char const **bear_strings_append(char const **in, char const *e);
78 static size_t bear_strings_length(char const *const *in);
79 static void bear_strings_release(char const **);
80 
81 static bear_env_t env_names = {ENV_OUTPUT, ENV_PRELOAD
82 #ifdef ENV_FLAT
83                                ,
84                                ENV_FLAT
85 #endif
86 };
87 
88 static bear_env_t initial_env = {0, 0
89 #ifdef ENV_FLAT
90                                  ,
91                                  0
92 #endif
93 };
94 
95 static int initialized = 0;
96 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
97 
98 static void on_load(void) __attribute__((constructor));
99 static void on_unload(void) __attribute__((destructor));
100 
101 #ifdef HAVE_EXECVE
102 static int call_execve(const char *path, char *const argv[],
103                        char *const envp[]);
104 #endif
105 #ifdef HAVE_EXECVP
106 static int call_execvp(const char *file, char *const argv[]);
107 #endif
108 #ifdef HAVE_EXECVPE
109 static int call_execvpe(const char *file, char *const argv[],
110                         char *const envp[]);
111 #endif
112 #ifdef HAVE_EXECVP2
113 static int call_execvP(const char *file, const char *search_path,
114                        char *const argv[]);
115 #endif
116 #ifdef HAVE_EXECT
117 static int call_exect(const char *path, char *const argv[], char *const envp[]);
118 #endif
119 #ifdef HAVE_POSIX_SPAWN
120 static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
121                             const posix_spawn_file_actions_t *file_actions,
122                             const posix_spawnattr_t *restrict attrp,
123                             char *const argv[restrict],
124                             char *const envp[restrict]);
125 #endif
126 #ifdef HAVE_POSIX_SPAWNP
127 static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
128                              const posix_spawn_file_actions_t *file_actions,
129                              const posix_spawnattr_t *restrict attrp,
130                              char *const argv[restrict],
131                              char *const envp[restrict]);
132 #endif
133 
134 /* Initialization method to Captures the relevant environment variables.
135  */
136 
on_load(void)137 static void on_load(void) {
138   pthread_mutex_lock(&mutex);
139   if (!initialized)
140     initialized = bear_capture_env_t(&initial_env);
141   pthread_mutex_unlock(&mutex);
142 }
143 
on_unload(void)144 static void on_unload(void) {
145   pthread_mutex_lock(&mutex);
146   bear_release_env_t(&initial_env);
147   initialized = 0;
148   pthread_mutex_unlock(&mutex);
149 }
150 
151 /* These are the methods we are try to hijack.
152  */
153 
154 #ifdef HAVE_EXECVE
execve(const char * path,char * const argv[],char * const envp[])155 int execve(const char *path, char *const argv[], char *const envp[]) {
156   bear_report_call(__func__, (char const *const *)argv);
157   return call_execve(path, argv, envp);
158 }
159 #endif
160 
161 #ifdef HAVE_EXECV
162 #ifndef HAVE_EXECVE
163 #error can not implement execv without execve
164 #endif
execv(const char * path,char * const argv[])165 int execv(const char *path, char *const argv[]) {
166   bear_report_call(__func__, (char const *const *)argv);
167   char *const *envp = bear_get_environment();
168   return call_execve(path, argv, envp);
169 }
170 #endif
171 
172 #ifdef HAVE_EXECVPE
execvpe(const char * file,char * const argv[],char * const envp[])173 int execvpe(const char *file, char *const argv[], char *const envp[]) {
174   bear_report_call(__func__, (char const *const *)argv);
175   return call_execvpe(file, argv, envp);
176 }
177 #endif
178 
179 #ifdef HAVE_EXECVP
execvp(const char * file,char * const argv[])180 int execvp(const char *file, char *const argv[]) {
181   bear_report_call(__func__, (char const *const *)argv);
182   return call_execvp(file, argv);
183 }
184 #endif
185 
186 #ifdef HAVE_EXECVP2
execvP(const char * file,const char * search_path,char * const argv[])187 int execvP(const char *file, const char *search_path, char *const argv[]) {
188   bear_report_call(__func__, (char const *const *)argv);
189   return call_execvP(file, search_path, argv);
190 }
191 #endif
192 
193 #ifdef HAVE_EXECT
exect(const char * path,char * const argv[],char * const envp[])194 int exect(const char *path, char *const argv[], char *const envp[]) {
195   bear_report_call(__func__, (char const *const *)argv);
196   return call_exect(path, argv, envp);
197 }
198 #endif
199 
200 #ifdef HAVE_EXECL
201 #ifndef HAVE_EXECVE
202 #error can not implement execl without execve
203 #endif
execl(const char * path,const char * arg,...)204 int execl(const char *path, const char *arg, ...) {
205   va_list args;
206   va_start(args, arg);
207   char const **argv = bear_strings_build(arg, &args);
208   va_end(args);
209 
210   bear_report_call(__func__, (char const *const *)argv);
211   char *const *envp = bear_get_environment();
212   int const result = call_execve(path, (char *const *)argv, envp);
213 
214   bear_strings_release(argv);
215   return result;
216 }
217 #endif
218 
219 #ifdef HAVE_EXECLP
220 #ifndef HAVE_EXECVP
221 #error can not implement execlp without execvp
222 #endif
execlp(const char * file,const char * arg,...)223 int execlp(const char *file, const char *arg, ...) {
224   va_list args;
225   va_start(args, arg);
226   char const **argv = bear_strings_build(arg, &args);
227   va_end(args);
228 
229   bear_report_call(__func__, (char const *const *)argv);
230   int const result = call_execvp(file, (char *const *)argv);
231 
232   bear_strings_release(argv);
233   return result;
234 }
235 #endif
236 
237 #ifdef HAVE_EXECLE
238 #ifndef HAVE_EXECVE
239 #error can not implement execle without execve
240 #endif
241 // int execle(const char *path, const char *arg, ..., char * const envp[]);
execle(const char * path,const char * arg,...)242 int execle(const char *path, const char *arg, ...) {
243   va_list args;
244   va_start(args, arg);
245   char const **argv = bear_strings_build(arg, &args);
246   char const **envp = va_arg(args, char const **);
247   va_end(args);
248 
249   bear_report_call(__func__, (char const *const *)argv);
250   int const result =
251       call_execve(path, (char *const *)argv, (char *const *)envp);
252 
253   bear_strings_release(argv);
254   return result;
255 }
256 #endif
257 
258 #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])259 int posix_spawn(pid_t *restrict pid, const char *restrict path,
260                 const posix_spawn_file_actions_t *file_actions,
261                 const posix_spawnattr_t *restrict attrp,
262                 char *const argv[restrict], char *const envp[restrict]) {
263   bear_report_call(__func__, (char const *const *)argv);
264   return call_posix_spawn(pid, path, file_actions, attrp, argv, envp);
265 }
266 #endif
267 
268 #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])269 int posix_spawnp(pid_t *restrict pid, const char *restrict file,
270                  const posix_spawn_file_actions_t *file_actions,
271                  const posix_spawnattr_t *restrict attrp,
272                  char *const argv[restrict], char *const envp[restrict]) {
273   bear_report_call(__func__, (char const *const *)argv);
274   return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp);
275 }
276 #endif
277 
278 /* These are the methods which forward the call to the standard implementation.
279  */
280 
281 #ifdef HAVE_EXECVE
call_execve(const char * path,char * const argv[],char * const envp[])282 static int call_execve(const char *path, char *const argv[],
283                        char *const envp[]) {
284   typedef int (*func)(const char *, char *const *, char *const *);
285 
286   DLSYM(func, fp, "execve");
287 
288   char const **const menvp = bear_update_environment(envp, &initial_env);
289   int const result = (*fp)(path, argv, (char *const *)menvp);
290   bear_strings_release(menvp);
291   return result;
292 }
293 #endif
294 
295 #ifdef HAVE_EXECVPE
call_execvpe(const char * file,char * const argv[],char * const envp[])296 static int call_execvpe(const char *file, char *const argv[],
297                         char *const envp[]) {
298   typedef int (*func)(const char *, char *const *, char *const *);
299 
300   DLSYM(func, fp, "execvpe");
301 
302   char const **const menvp = bear_update_environment(envp, &initial_env);
303   int const result = (*fp)(file, argv, (char *const *)menvp);
304   bear_strings_release(menvp);
305   return result;
306 }
307 #endif
308 
309 #ifdef HAVE_EXECVP
call_execvp(const char * file,char * const argv[])310 static int call_execvp(const char *file, char *const argv[]) {
311   typedef int (*func)(const char *file, char *const argv[]);
312 
313   DLSYM(func, fp, "execvp");
314 
315   bear_env_t current_env;
316   bear_capture_env_t(&current_env);
317   bear_reset_env_t(&initial_env);
318   int const result = (*fp)(file, argv);
319   bear_reset_env_t(&current_env);
320   bear_release_env_t(&current_env);
321 
322   return result;
323 }
324 #endif
325 
326 #ifdef HAVE_EXECVP2
call_execvP(const char * file,const char * search_path,char * const argv[])327 static int call_execvP(const char *file, const char *search_path,
328                        char *const argv[]) {
329   typedef int (*func)(const char *, const char *, char *const *);
330 
331   DLSYM(func, fp, "execvP");
332 
333   bear_env_t current_env;
334   bear_capture_env_t(&current_env);
335   bear_reset_env_t(&initial_env);
336   int const result = (*fp)(file, search_path, argv);
337   bear_reset_env_t(&current_env);
338   bear_release_env_t(&current_env);
339 
340   return result;
341 }
342 #endif
343 
344 #ifdef HAVE_EXECT
call_exect(const char * path,char * const argv[],char * const envp[])345 static int call_exect(const char *path, char *const argv[],
346                       char *const envp[]) {
347   typedef int (*func)(const char *, char *const *, char *const *);
348 
349   DLSYM(func, fp, "exect");
350 
351   char const **const menvp = bear_update_environment(envp, &initial_env);
352   int const result = (*fp)(path, argv, (char *const *)menvp);
353   bear_strings_release(menvp);
354   return result;
355 }
356 #endif
357 
358 #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])359 static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
360                             const posix_spawn_file_actions_t *file_actions,
361                             const posix_spawnattr_t *restrict attrp,
362                             char *const argv[restrict],
363                             char *const envp[restrict]) {
364   typedef int (*func)(pid_t *restrict, const char *restrict,
365                       const posix_spawn_file_actions_t *,
366                       const posix_spawnattr_t *restrict, char *const *restrict,
367                       char *const *restrict);
368 
369   DLSYM(func, fp, "posix_spawn");
370 
371   char const **const menvp = bear_update_environment(envp, &initial_env);
372   int const result =
373       (*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp);
374   bear_strings_release(menvp);
375   return result;
376 }
377 #endif
378 
379 #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])380 static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
381                              const posix_spawn_file_actions_t *file_actions,
382                              const posix_spawnattr_t *restrict attrp,
383                              char *const argv[restrict],
384                              char *const envp[restrict]) {
385   typedef int (*func)(pid_t *restrict, const char *restrict,
386                       const posix_spawn_file_actions_t *,
387                       const posix_spawnattr_t *restrict, char *const *restrict,
388                       char *const *restrict);
389 
390   DLSYM(func, fp, "posix_spawnp");
391 
392   char const **const menvp = bear_update_environment(envp, &initial_env);
393   int const result =
394       (*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp);
395   bear_strings_release(menvp);
396   return result;
397 }
398 #endif
399 
400 /* this method is to write log about the process creation. */
401 
bear_report_call(char const * fun,char const * const argv[])402 static void bear_report_call(char const *fun, char const *const argv[]) {
403   static int const GS = 0x1d;
404   static int const RS = 0x1e;
405   static int const US = 0x1f;
406 
407   if (!initialized)
408     return;
409 
410   pthread_mutex_lock(&mutex);
411   const char *cwd = getcwd(NULL, 0);
412   if (0 == cwd) {
413     perror("bear: getcwd");
414     pthread_mutex_unlock(&mutex);
415     exit(EXIT_FAILURE);
416   }
417   char const *const out_dir = initial_env[0];
418   size_t const path_max_length = strlen(out_dir) + 32;
419   char filename[path_max_length];
420   if (-1 ==
421       snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) {
422     perror("bear: snprintf");
423     pthread_mutex_unlock(&mutex);
424     exit(EXIT_FAILURE);
425   }
426   FILE *fd = fopen(filename, "a+");
427   if (0 == fd) {
428     perror("bear: fopen");
429     pthread_mutex_unlock(&mutex);
430     exit(EXIT_FAILURE);
431   }
432   fprintf(fd, "%d%c", getpid(), RS);
433   fprintf(fd, "%d%c", getppid(), RS);
434   fprintf(fd, "%s%c", fun, RS);
435   fprintf(fd, "%s%c", cwd, RS);
436   size_t const argc = bear_strings_length(argv);
437   for (size_t it = 0; it < argc; ++it) {
438     fprintf(fd, "%s%c", argv[it], US);
439   }
440   fprintf(fd, "%c", GS);
441   if (fclose(fd)) {
442     perror("bear: fclose");
443     pthread_mutex_unlock(&mutex);
444     exit(EXIT_FAILURE);
445   }
446   free((void *)cwd);
447   pthread_mutex_unlock(&mutex);
448 }
449 
450 /* update environment assure that children 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[],
484                                             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,
492                                         char const *const value) {
493   // find the key if it's there
494   size_t const key_length = strlen(key);
495   char const **it = envs;
496   for (; (it) && (*it); ++it) {
497     if ((0 == strncmp(*it, key, key_length)) && (strlen(*it) > key_length) &&
498         ('=' == (*it)[key_length]))
499       break;
500   }
501   // allocate a environment entry
502   size_t const value_length = strlen(value);
503   size_t const env_length = key_length + value_length + 2;
504   char *env = malloc(env_length);
505   if (0 == env) {
506     perror("bear: malloc [in env_update]");
507     exit(EXIT_FAILURE);
508   }
509   if (-1 == snprintf(env, env_length, "%s=%s", key, value)) {
510     perror("bear: snprintf");
511     exit(EXIT_FAILURE);
512   }
513   // replace or append the environment entry
514   if (it && *it) {
515     free((void *)*it);
516     *it = env;
517     return envs;
518   }
519   return bear_strings_append(envs, env);
520 }
521 
bear_get_environment()522 static char **bear_get_environment() {
523 #if defined HAVE_NSGETENVIRON
524   return *_NSGetEnviron();
525 #else
526   return environ;
527 #endif
528 }
529 
530 /* util methods to deal with string arrays. environment and process arguments
531  * are both represented as string arrays. */
532 
bear_strings_build(char const * const arg,va_list * args)533 static char const **bear_strings_build(char const *const arg, va_list *args) {
534   char const **result = 0;
535   size_t size = 0;
536   for (char const *it = arg; it; it = va_arg(*args, char const *)) {
537     result = realloc(result, (size + 1) * sizeof(char const *));
538     if (0 == result) {
539       perror("bear: realloc");
540       exit(EXIT_FAILURE);
541     }
542     char const *copy = strdup(it);
543     if (0 == copy) {
544       perror("bear: strdup");
545       exit(EXIT_FAILURE);
546     }
547     result[size++] = copy;
548   }
549   result = realloc(result, (size + 1) * sizeof(char const *));
550   if (0 == result) {
551     perror("bear: realloc");
552     exit(EXIT_FAILURE);
553   }
554   result[size++] = 0;
555 
556   return result;
557 }
558 
bear_strings_copy(char const ** const in)559 static char const **bear_strings_copy(char const **const in) {
560   size_t const size = bear_strings_length(in);
561 
562   char const **const result = malloc((size + 1) * sizeof(char const *));
563   if (0 == result) {
564     perror("bear: malloc");
565     exit(EXIT_FAILURE);
566   }
567 
568   char const **out_it = result;
569   for (char const *const *in_it = in; (in_it) && (*in_it); ++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