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 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 }