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(¤t_env);
317 bear_reset_env_t(&initial_env);
318 int const result = (*fp)(file, argv);
319 bear_reset_env_t(¤t_env);
320 bear_release_env_t(¤t_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(¤t_env);
335 bear_reset_env_t(&initial_env);
336 int const result = (*fp)(file, search_path, argv);
337 bear_reset_env_t(¤t_env);
338 bear_release_env_t(¤t_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