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 
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 
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
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
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
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
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
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
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
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
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[]);
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
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
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
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
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
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
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
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
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
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 
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     exit(EXIT_FAILURE);
415   }
416   char const *const out_dir = initial_env[0];
417   size_t const path_max_length = strlen(out_dir) + 32;
418   char filename[path_max_length];
419   if (-1 ==
420       snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) {
421     perror("bear: snprintf");
422     exit(EXIT_FAILURE);
423   }
424   FILE *fd = fopen(filename, "a+");
425   if (0 == fd) {
426     perror("bear: fopen");
427     exit(EXIT_FAILURE);
428   }
429   fprintf(fd, "%d%c", getpid(), RS);
430   fprintf(fd, "%d%c", getppid(), RS);
431   fprintf(fd, "%s%c", fun, RS);
432   fprintf(fd, "%s%c", cwd, RS);
433   size_t const argc = bear_strings_length(argv);
434   for (size_t it = 0; it < argc; ++it) {
435     fprintf(fd, "%s%c", argv[it], US);
436   }
437   fprintf(fd, "%c", GS);
438   if (fclose(fd)) {
439     perror("bear: fclose");
440     exit(EXIT_FAILURE);
441   }
442   free((void *)cwd);
443   pthread_mutex_unlock(&mutex);
444 }
445 
446 /* update environment assure that chilren processes will copy the desired
447  * behaviour */
448 
449 static int bear_capture_env_t(bear_env_t *env) {
450   int status = 1;
451   for (size_t it = 0; it < ENV_SIZE; ++it) {
452     char const *const env_value = getenv(env_names[it]);
453     char const *const env_copy = (env_value) ? strdup(env_value) : env_value;
454     (*env)[it] = env_copy;
455     status &= (env_copy) ? 1 : 0;
456   }
457   return status;
458 }
459 
460 static int bear_reset_env_t(bear_env_t *env) {
461   int status = 1;
462   for (size_t it = 0; it < ENV_SIZE; ++it) {
463     if ((*env)[it]) {
464       setenv(env_names[it], (*env)[it], 1);
465     } else {
466       unsetenv(env_names[it]);
467     }
468   }
469   return status;
470 }
471 
472 static void bear_release_env_t(bear_env_t *env) {
473   for (size_t it = 0; it < ENV_SIZE; ++it) {
474     free((void *)(*env)[it]);
475     (*env)[it] = 0;
476   }
477 }
478 
479 static char const **bear_update_environment(char *const envp[],
480                                             bear_env_t *env) {
481   char const **result = bear_strings_copy((char const **)envp);
482   for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it)
483     result = bear_update_environ(result, env_names[it], (*env)[it]);
484   return result;
485 }
486 
487 static char const **bear_update_environ(char const *envs[], char const *key,
488                                         char const *const value) {
489   // find the key if it's there
490   size_t const key_length = strlen(key);
491   char const **it = envs;
492   for (; (it) && (*it); ++it) {
493     if ((0 == strncmp(*it, key, key_length)) && (strlen(*it) > key_length) &&
494         ('=' == (*it)[key_length]))
495       break;
496   }
497   // allocate a environment entry
498   size_t const value_length = strlen(value);
499   size_t const env_length = key_length + value_length + 2;
500   char *env = malloc(env_length);
501   if (0 == env) {
502     perror("bear: malloc [in env_update]");
503     exit(EXIT_FAILURE);
504   }
505   if (-1 == snprintf(env, env_length, "%s=%s", key, value)) {
506     perror("bear: snprintf");
507     exit(EXIT_FAILURE);
508   }
509   // replace or append the environment entry
510   if (it && *it) {
511     free((void *)*it);
512     *it = env;
513     return envs;
514   }
515   return bear_strings_append(envs, env);
516 }
517 
518 static char **bear_get_environment() {
519 #if defined HAVE_NSGETENVIRON
520   return *_NSGetEnviron();
521 #else
522   return environ;
523 #endif
524 }
525 
526 /* util methods to deal with string arrays. environment and process arguments
527  * are both represented as string arrays. */
528 
529 static char const **bear_strings_build(char const *const arg, va_list *args) {
530   char const **result = 0;
531   size_t size = 0;
532   for (char const *it = arg; it; it = va_arg(*args, char const *)) {
533     result = realloc(result, (size + 1) * sizeof(char const *));
534     if (0 == result) {
535       perror("bear: realloc");
536       exit(EXIT_FAILURE);
537     }
538     char const *copy = strdup(it);
539     if (0 == copy) {
540       perror("bear: strdup");
541       exit(EXIT_FAILURE);
542     }
543     result[size++] = copy;
544   }
545   result = realloc(result, (size + 1) * sizeof(char const *));
546   if (0 == result) {
547     perror("bear: realloc");
548     exit(EXIT_FAILURE);
549   }
550   result[size++] = 0;
551 
552   return result;
553 }
554 
555 static char const **bear_strings_copy(char const **const in) {
556   size_t const size = bear_strings_length(in);
557 
558   char const **const result = malloc((size + 1) * sizeof(char const *));
559   if (0 == result) {
560     perror("bear: malloc");
561     exit(EXIT_FAILURE);
562   }
563 
564   char const **out_it = result;
565   for (char const *const *in_it = in; (in_it) && (*in_it); ++in_it, ++out_it) {
566     *out_it = strdup(*in_it);
567     if (0 == *out_it) {
568       perror("bear: strdup");
569       exit(EXIT_FAILURE);
570     }
571   }
572   *out_it = 0;
573   return result;
574 }
575 
576 static char const **bear_strings_append(char const **const in,
577                                         char const *const e) {
578   size_t size = bear_strings_length(in);
579   char const **result = realloc(in, (size + 2) * sizeof(char const *));
580   if (0 == result) {
581     perror("bear: realloc");
582     exit(EXIT_FAILURE);
583   }
584   result[size++] = e;
585   result[size++] = 0;
586   return result;
587 }
588 
589 static size_t bear_strings_length(char const *const *const in) {
590   size_t result = 0;
591   for (char const *const *it = in; (it) && (*it); ++it)
592     ++result;
593   return result;
594 }
595 
596 static void bear_strings_release(char const **in) {
597   for (char const *const *it = in; (it) && (*it); ++it) {
598     free((void *)*it);
599   }
600   free((void *)in);
601 }