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(¤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 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 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 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 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 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 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 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 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 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 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 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 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 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 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