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
40extern 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
64typedef char const *bear_env_t[ENV_SIZE];
65
66static int bear_capture_env_t(bear_env_t *env);
67static int bear_reset_env_t(bear_env_t *env);
68static void bear_release_env_t(bear_env_t *env);
69static char const **bear_update_environment(char *const envp[],
70 bear_env_t *env);
71static char const **bear_update_environ(char const **in, char const *key,
72 char const *value);
73static char **bear_get_environment();
74static void bear_report_call(char const *fun, char const *const argv[]);
75static char const **bear_strings_build(char const *arg, va_list *ap);
76static char const **bear_strings_copy(char const **const in);
77static char const **bear_strings_append(char const **in, char const *e);
78static size_t bear_strings_length(char const *const *in);
79static void bear_strings_release(char const **);
80
81static bear_env_t env_names = {ENV_OUTPUT, ENV_PRELOAD
82#ifdef ENV_FLAT
83 ,
84 ENV_FLAT
85#endif
86};
87
88static bear_env_t initial_env = {0, 0
89#ifdef ENV_FLAT
90 ,
91 0
92#endif
93};
94
95static int initialized = 0;
96static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
97
98static void on_load(void) __attribute__((constructor));
99static void on_unload(void) __attribute__((destructor));
100
101#ifdef HAVE_EXECVE
102static int call_execve(const char *path, char *const argv[],
103 char *const envp[]);
104#endif
105#ifdef HAVE_EXECVP
106static int call_execvp(const char *file, char *const argv[]);
107#endif
108#ifdef HAVE_EXECVPE
109static int call_execvpe(const char *file, char *const argv[],
110 char *const envp[]);
111#endif
112#ifdef HAVE_EXECVP2
113static int call_execvP(const char *file, const char *search_path,
114 char *const argv[]);
115#endif
116#ifdef HAVE_EXECT
117static int call_exect(const char *path, char *const argv[], char *const envp[]);
118#endif
119#ifdef HAVE_POSIX_SPAWN
120static 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
127static 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
137static void on_load(void) {
138 pthread_mutex_lock(mutex: &mutex);
139 if (!initialized)
140 initialized = bear_capture_env_t(env: &initial_env);
141 pthread_mutex_unlock(mutex: &mutex);
142}
143
144static void on_unload(void) {
145 pthread_mutex_lock(mutex: &mutex);
146 bear_release_env_t(env: &initial_env);
147 initialized = 0;
148 pthread_mutex_unlock(mutex: &mutex);
149}
150
151/* These are the methods we are try to hijack.
152 */
153
154#ifdef HAVE_EXECVE
155int 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
165int 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
173int 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
180int 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
187int 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
194int 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
204int 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
223int 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[]);
242int 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
259int 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
269int 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
282static 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
296static 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
310static 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
327static 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
345static 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
359static 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
380static 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
402static 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: &mutex);
411 const char *cwd = getcwd(NULL, size: 0);
412 if (0 == cwd) {
413 perror(s: "bear: getcwd");
414 pthread_mutex_unlock(mutex: &mutex);
415 exit(EXIT_FAILURE);
416 }
417 char const *const out_dir = initial_env[0];
418 size_t const path_max_length = strlen(s: out_dir) + 32;
419 char filename[path_max_length];
420 if (-1 ==
421 snprintf(s: filename, maxlen: path_max_length, format: "%s/%d.cmd", out_dir, getpid())) {
422 perror(s: "bear: snprintf");
423 pthread_mutex_unlock(mutex: &mutex);
424 exit(EXIT_FAILURE);
425 }
426 FILE *fd = fopen(filename: filename, modes: "a+");
427 if (0 == fd) {
428 perror(s: "bear: fopen");
429 pthread_mutex_unlock(mutex: &mutex);
430 exit(EXIT_FAILURE);
431 }
432 fprintf(stream: fd, format: "%d%c", getpid(), RS);
433 fprintf(stream: fd, format: "%d%c", getppid(), RS);
434 fprintf(stream: fd, format: "%s%c", fun, RS);
435 fprintf(stream: fd, format: "%s%c", cwd, RS);
436 size_t const argc = bear_strings_length(in: argv);
437 for (size_t it = 0; it < argc; ++it) {
438 fprintf(stream: fd, format: "%s%c", argv[it], US);
439 }
440 fprintf(stream: fd, format: "%c", GS);
441 if (fclose(stream: fd)) {
442 perror(s: "bear: fclose");
443 pthread_mutex_unlock(mutex: &mutex);
444 exit(EXIT_FAILURE);
445 }
446 free(ptr: (void *)cwd);
447 pthread_mutex_unlock(mutex: &mutex);
448}
449
450/* update environment assure that children processes will copy the desired
451 * behaviour */
452
453static 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(name: env_names[it]);
457 char const *const env_copy = (env_value) ? strdup(s: env_value) : env_value;
458 (*env)[it] = env_copy;
459 status &= (env_copy) ? 1 : 0;
460 }
461 return status;
462}
463
464static 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(name: env_names[it], value: (*env)[it], replace: 1);
469 } else {
470 unsetenv(name: env_names[it]);
471 }
472 }
473 return status;
474}
475
476static void bear_release_env_t(bear_env_t *env) {
477 for (size_t it = 0; it < ENV_SIZE; ++it) {
478 free(ptr: (void *)(*env)[it]);
479 (*env)[it] = 0;
480 }
481}
482
483static char const **bear_update_environment(char *const envp[],
484 bear_env_t *env) {
485 char const **result = bear_strings_copy(in: (char const **)envp);
486 for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it)
487 result = bear_update_environ(in: result, key: env_names[it], value: (*env)[it]);
488 return result;
489}
490
491static 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(s: key);
495 char const **it = envs;
496 for (; (it) && (*it); ++it) {
497 if ((0 == strncmp(s1: *it, s2: key, n: key_length)) && (strlen(s: *it) > key_length) &&
498 ('=' == (*it)[key_length]))
499 break;
500 }
501 // allocate a environment entry
502 size_t const value_length = strlen(s: value);
503 size_t const env_length = key_length + value_length + 2;
504 char *env = malloc(size: env_length);
505 if (0 == env) {
506 perror(s: "bear: malloc [in env_update]");
507 exit(EXIT_FAILURE);
508 }
509 if (-1 == snprintf(s: env, maxlen: env_length, format: "%s=%s", key, value)) {
510 perror(s: "bear: snprintf");
511 exit(EXIT_FAILURE);
512 }
513 // replace or append the environment entry
514 if (it && *it) {
515 free(ptr: (void *)*it);
516 *it = env;
517 return envs;
518 }
519 return bear_strings_append(in: envs, e: env);
520}
521
522static 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
533static 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(ptr: result, size: (size + 1) * sizeof(char const *));
538 if (0 == result) {
539 perror(s: "bear: realloc");
540 exit(EXIT_FAILURE);
541 }
542 char const *copy = strdup(s: it);
543 if (0 == copy) {
544 perror(s: "bear: strdup");
545 exit(EXIT_FAILURE);
546 }
547 result[size++] = copy;
548 }
549 result = realloc(ptr: result, size: (size + 1) * sizeof(char const *));
550 if (0 == result) {
551 perror(s: "bear: realloc");
552 exit(EXIT_FAILURE);
553 }
554 result[size++] = 0;
555
556 return result;
557}
558
559static 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: (size + 1) * sizeof(char const *));
563 if (0 == result) {
564 perror(s: "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(s: *in_it);
571 if (0 == *out_it) {
572 perror(s: "bear: strdup");
573 exit(EXIT_FAILURE);
574 }
575 }
576 *out_it = 0;
577 return result;
578}
579
580static 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(ptr: in, size: (size + 2) * sizeof(char const *));
584 if (0 == result) {
585 perror(s: "bear: realloc");
586 exit(EXIT_FAILURE);
587 }
588 result[size++] = e;
589 result[size++] = 0;
590 return result;
591}
592
593static 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
600static void bear_strings_release(char const **in) {
601 for (char const *const *it = in; (it) && (*it); ++it) {
602 free(ptr: (void *)*it);
603 }
604 free(ptr: (void *)in);
605}
606

source code of clang/tools/scan-build-py/lib/libear/ear.c