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 #include "config.h" 22 23 #include <stddef.h> 24 #include <stdarg.h> 25 #include <stdlib.h> 26 #include <stdio.h> 27 #include <string.h> 28 #include <unistd.h> 29 #include <dlfcn.h> 30 #include <pthread.h> 31 32 #if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP 33 #include <spawn.h> 34 #endif 35 36 #if defined HAVE_NSGETENVIRON 37 # include <crt_externs.h> 38 #else 39 extern char **environ; 40 #endif 41 42 #define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR" 43 #ifdef APPLE 44 # define ENV_FLAT "DYLD_FORCE_FLAT_NAMESPACE" 45 # define ENV_PRELOAD "DYLD_INSERT_LIBRARIES" 46 # define ENV_SIZE 3 47 #else 48 # define ENV_PRELOAD "LD_PRELOAD" 49 # define ENV_SIZE 2 50 #endif 51 52 #define DLSYM(TYPE_, VAR_, SYMBOL_) \ 53 union { \ 54 void *from; \ 55 TYPE_ to; \ 56 } cast; \ 57 if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) { \ 58 perror("bear: dlsym"); \ 59 exit(EXIT_FAILURE); \ 60 } \ 61 TYPE_ const VAR_ = cast.to; 62 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[], bear_env_t *env); 70 static char const **bear_update_environ(char const **in, char const *key, char const *value); 71 static char **bear_get_environment(); 72 static void bear_report_call(char const *fun, char const *const argv[]); 73 static char const **bear_strings_build(char const *arg, va_list *ap); 74 static char const **bear_strings_copy(char const **const in); 75 static char const **bear_strings_append(char const **in, char const *e); 76 static size_t bear_strings_length(char const *const *in); 77 static void bear_strings_release(char const **); 78 79 80 static bear_env_t env_names = 81 { ENV_OUTPUT 82 , ENV_PRELOAD 83 #ifdef ENV_FLAT 84 , ENV_FLAT 85 #endif 86 }; 87 88 static bear_env_t initial_env = 89 { 0 90 , 0 91 #ifdef ENV_FLAT 92 , 0 93 #endif 94 }; 95 96 static int initialized = 0; 97 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 98 99 static void on_load(void) __attribute__((constructor)); 100 static void on_unload(void) __attribute__((destructor)); 101 102 103 #ifdef HAVE_EXECVE 104 static int call_execve(const char *path, char *const argv[], 105 char *const envp[]); 106 #endif 107 #ifdef HAVE_EXECVP 108 static int call_execvp(const char *file, char *const argv[]); 109 #endif 110 #ifdef HAVE_EXECVPE 111 static int call_execvpe(const char *file, char *const argv[], 112 char *const envp[]); 113 #endif 114 #ifdef HAVE_EXECVP2 115 static int call_execvP(const char *file, const char *search_path, 116 char *const argv[]); 117 #endif 118 #ifdef HAVE_EXECT 119 static int call_exect(const char *path, char *const argv[], 120 char *const envp[]); 121 #endif 122 #ifdef HAVE_POSIX_SPAWN 123 static int call_posix_spawn(pid_t *restrict pid, const char *restrict path, 124 const posix_spawn_file_actions_t *file_actions, 125 const posix_spawnattr_t *restrict attrp, 126 char *const argv[restrict], 127 char *const envp[restrict]); 128 #endif 129 #ifdef HAVE_POSIX_SPAWNP 130 static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file, 131 const posix_spawn_file_actions_t *file_actions, 132 const posix_spawnattr_t *restrict attrp, 133 char *const argv[restrict], 134 char *const envp[restrict]); 135 #endif 136 137 138 /* Initialization method to Captures the relevant environment variables. 139 */ 140 141 static void on_load(void) { 142 pthread_mutex_lock(&mutex); 143 if (!initialized) 144 initialized = bear_capture_env_t(&initial_env); 145 pthread_mutex_unlock(&mutex); 146 } 147 148 static void on_unload(void) { 149 pthread_mutex_lock(&mutex); 150 bear_release_env_t(&initial_env); 151 initialized = 0; 152 pthread_mutex_unlock(&mutex); 153 } 154 155 156 /* These are the methods we are try to hijack. 157 */ 158 159 #ifdef HAVE_EXECVE 160 int execve(const char *path, char *const argv[], char *const envp[]) { 161 bear_report_call(__func__, (char const *const *)argv); 162 return call_execve(path, argv, envp); 163 } 164 #endif 165 166 #ifdef HAVE_EXECV 167 #ifndef HAVE_EXECVE 168 #error can not implement execv without execve 169 #endif 170 int execv(const char *path, char *const argv[]) { 171 bear_report_call(__func__, (char const *const *)argv); 172 char * const * envp = bear_get_environment(); 173 return call_execve(path, argv, envp); 174 } 175 #endif 176 177 #ifdef HAVE_EXECVPE 178 int execvpe(const char *file, char *const argv[], char *const envp[]) { 179 bear_report_call(__func__, (char const *const *)argv); 180 return call_execvpe(file, argv, envp); 181 } 182 #endif 183 184 #ifdef HAVE_EXECVP 185 int execvp(const char *file, char *const argv[]) { 186 bear_report_call(__func__, (char const *const *)argv); 187 return call_execvp(file, argv); 188 } 189 #endif 190 191 #ifdef HAVE_EXECVP2 192 int execvP(const char *file, const char *search_path, char *const argv[]) { 193 bear_report_call(__func__, (char const *const *)argv); 194 return call_execvP(file, search_path, argv); 195 } 196 #endif 197 198 #ifdef HAVE_EXECT 199 int exect(const char *path, char *const argv[], char *const envp[]) { 200 bear_report_call(__func__, (char const *const *)argv); 201 return call_exect(path, argv, envp); 202 } 203 #endif 204 205 #ifdef HAVE_EXECL 206 # ifndef HAVE_EXECVE 207 # error can not implement execl without execve 208 # endif 209 int execl(const char *path, const char *arg, ...) { 210 va_list args; 211 va_start(args, arg); 212 char const **argv = bear_strings_build(arg, &args); 213 va_end(args); 214 215 bear_report_call(__func__, (char const *const *)argv); 216 char * const * envp = bear_get_environment(); 217 int const result = call_execve(path, (char *const *)argv, envp); 218 219 bear_strings_release(argv); 220 return result; 221 } 222 #endif 223 224 #ifdef HAVE_EXECLP 225 # ifndef HAVE_EXECVP 226 # error can not implement execlp without execvp 227 # endif 228 int execlp(const char *file, const char *arg, ...) { 229 va_list args; 230 va_start(args, arg); 231 char const **argv = bear_strings_build(arg, &args); 232 va_end(args); 233 234 bear_report_call(__func__, (char const *const *)argv); 235 int const result = call_execvp(file, (char *const *)argv); 236 237 bear_strings_release(argv); 238 return result; 239 } 240 #endif 241 242 #ifdef HAVE_EXECLE 243 # ifndef HAVE_EXECVE 244 # error can not implement execle without execve 245 # endif 246 // int execle(const char *path, const char *arg, ..., char * const envp[]); 247 int execle(const char *path, const char *arg, ...) { 248 va_list args; 249 va_start(args, arg); 250 char const **argv = bear_strings_build(arg, &args); 251 char const **envp = va_arg(args, char const **); 252 va_end(args); 253 254 bear_report_call(__func__, (char const *const *)argv); 255 int const result = 256 call_execve(path, (char *const *)argv, (char *const *)envp); 257 258 bear_strings_release(argv); 259 return result; 260 } 261 #endif 262 263 #ifdef HAVE_POSIX_SPAWN 264 int posix_spawn(pid_t *restrict pid, const char *restrict path, 265 const posix_spawn_file_actions_t *file_actions, 266 const posix_spawnattr_t *restrict attrp, 267 char *const argv[restrict], char *const envp[restrict]) { 268 bear_report_call(__func__, (char const *const *)argv); 269 return call_posix_spawn(pid, path, file_actions, attrp, argv, envp); 270 } 271 #endif 272 273 #ifdef HAVE_POSIX_SPAWNP 274 int posix_spawnp(pid_t *restrict pid, const char *restrict file, 275 const posix_spawn_file_actions_t *file_actions, 276 const posix_spawnattr_t *restrict attrp, 277 char *const argv[restrict], char *const envp[restrict]) { 278 bear_report_call(__func__, (char const *const *)argv); 279 return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp); 280 } 281 #endif 282 283 /* These are the methods which forward the call to the standard implementation. 284 */ 285 286 #ifdef HAVE_EXECVE 287 static int call_execve(const char *path, char *const argv[], 288 char *const envp[]) { 289 typedef int (*func)(const char *, char *const *, char *const *); 290 291 DLSYM(func, fp, "execve"); 292 293 char const **const menvp = bear_update_environment(envp, &initial_env); 294 int const result = (*fp)(path, argv, (char *const *)menvp); 295 bear_strings_release(menvp); 296 return result; 297 } 298 #endif 299 300 #ifdef HAVE_EXECVPE 301 static int call_execvpe(const char *file, char *const argv[], 302 char *const envp[]) { 303 typedef int (*func)(const char *, char *const *, char *const *); 304 305 DLSYM(func, fp, "execvpe"); 306 307 char const **const menvp = bear_update_environment(envp, &initial_env); 308 int const result = (*fp)(file, argv, (char *const *)menvp); 309 bear_strings_release(menvp); 310 return result; 311 } 312 #endif 313 314 #ifdef HAVE_EXECVP 315 static int call_execvp(const char *file, char *const argv[]) { 316 typedef int (*func)(const char *file, char *const argv[]); 317 318 DLSYM(func, fp, "execvp"); 319 320 bear_env_t current_env; 321 bear_capture_env_t(¤t_env); 322 bear_reset_env_t(&initial_env); 323 int const result = (*fp)(file, argv); 324 bear_reset_env_t(¤t_env); 325 bear_release_env_t(¤t_env); 326 327 return result; 328 } 329 #endif 330 331 #ifdef HAVE_EXECVP2 332 static int call_execvP(const char *file, const char *search_path, 333 char *const argv[]) { 334 typedef int (*func)(const char *, const char *, char *const *); 335 336 DLSYM(func, fp, "execvP"); 337 338 bear_env_t current_env; 339 bear_capture_env_t(¤t_env); 340 bear_reset_env_t(&initial_env); 341 int const result = (*fp)(file, search_path, argv); 342 bear_reset_env_t(¤t_env); 343 bear_release_env_t(¤t_env); 344 345 return result; 346 } 347 #endif 348 349 #ifdef HAVE_EXECT 350 static int call_exect(const char *path, char *const argv[], 351 char *const envp[]) { 352 typedef int (*func)(const char *, char *const *, char *const *); 353 354 DLSYM(func, fp, "exect"); 355 356 char const **const menvp = bear_update_environment(envp, &initial_env); 357 int const result = (*fp)(path, argv, (char *const *)menvp); 358 bear_strings_release(menvp); 359 return result; 360 } 361 #endif 362 363 #ifdef HAVE_POSIX_SPAWN 364 static int call_posix_spawn(pid_t *restrict pid, const char *restrict path, 365 const posix_spawn_file_actions_t *file_actions, 366 const posix_spawnattr_t *restrict attrp, 367 char *const argv[restrict], 368 char *const envp[restrict]) { 369 typedef int (*func)(pid_t *restrict, const char *restrict, 370 const posix_spawn_file_actions_t *, 371 const posix_spawnattr_t *restrict, 372 char *const *restrict, char *const *restrict); 373 374 DLSYM(func, fp, "posix_spawn"); 375 376 char const **const menvp = bear_update_environment(envp, &initial_env); 377 int const result = 378 (*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp); 379 bear_strings_release(menvp); 380 return result; 381 } 382 #endif 383 384 #ifdef HAVE_POSIX_SPAWNP 385 static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file, 386 const posix_spawn_file_actions_t *file_actions, 387 const posix_spawnattr_t *restrict attrp, 388 char *const argv[restrict], 389 char *const envp[restrict]) { 390 typedef int (*func)(pid_t *restrict, const char *restrict, 391 const posix_spawn_file_actions_t *, 392 const posix_spawnattr_t *restrict, 393 char *const *restrict, char *const *restrict); 394 395 DLSYM(func, fp, "posix_spawnp"); 396 397 char const **const menvp = bear_update_environment(envp, &initial_env); 398 int const result = 399 (*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp); 400 bear_strings_release(menvp); 401 return result; 402 } 403 #endif 404 405 /* this method is to write log about the process creation. */ 406 407 static void bear_report_call(char const *fun, char const *const argv[]) { 408 static int const GS = 0x1d; 409 static int const RS = 0x1e; 410 static int const US = 0x1f; 411 412 if (!initialized) 413 return; 414 415 pthread_mutex_lock(&mutex); 416 const char *cwd = getcwd(NULL, 0); 417 if (0 == cwd) { 418 perror("bear: getcwd"); 419 exit(EXIT_FAILURE); 420 } 421 char const * const out_dir = initial_env[0]; 422 size_t const path_max_length = strlen(out_dir) + 32; 423 char filename[path_max_length]; 424 if (-1 == snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) { 425 perror("bear: snprintf"); 426 exit(EXIT_FAILURE); 427 } 428 FILE * fd = fopen(filename, "a+"); 429 if (0 == fd) { 430 perror("bear: fopen"); 431 exit(EXIT_FAILURE); 432 } 433 fprintf(fd, "%d%c", getpid(), RS); 434 fprintf(fd, "%d%c", getppid(), RS); 435 fprintf(fd, "%s%c", fun, RS); 436 fprintf(fd, "%s%c", cwd, RS); 437 size_t const argc = bear_strings_length(argv); 438 for (size_t it = 0; it < argc; ++it) { 439 fprintf(fd, "%s%c", argv[it], US); 440 } 441 fprintf(fd, "%c", GS); 442 if (fclose(fd)) { 443 perror("bear: fclose"); 444 exit(EXIT_FAILURE); 445 } 446 free((void *)cwd); 447 pthread_mutex_unlock(&mutex); 448 } 449 450 /* update environment assure that chilren 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[], bear_env_t *env) { 484 char const **result = bear_strings_copy((char const **)envp); 485 for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it) 486 result = bear_update_environ(result, env_names[it], (*env)[it]); 487 return result; 488 } 489 490 static char const **bear_update_environ(char const *envs[], char const *key, char const * const value) { 491 // find the key if it's there 492 size_t const key_length = strlen(key); 493 char const **it = envs; 494 for (; (it) && (*it); ++it) { 495 if ((0 == strncmp(*it, key, key_length)) && 496 (strlen(*it) > key_length) && ('=' == (*it)[key_length])) 497 break; 498 } 499 // allocate a environment entry 500 size_t const value_length = strlen(value); 501 size_t const env_length = key_length + value_length + 2; 502 char *env = malloc(env_length); 503 if (0 == env) { 504 perror("bear: malloc [in env_update]"); 505 exit(EXIT_FAILURE); 506 } 507 if (-1 == snprintf(env, env_length, "%s=%s", key, value)) { 508 perror("bear: snprintf"); 509 exit(EXIT_FAILURE); 510 } 511 // replace or append the environment entry 512 if (it && *it) { 513 free((void *)*it); 514 *it = env; 515 return envs; 516 } 517 return bear_strings_append(envs, env); 518 } 519 520 static char **bear_get_environment() { 521 #if defined HAVE_NSGETENVIRON 522 return *_NSGetEnviron(); 523 #else 524 return environ; 525 #endif 526 } 527 528 /* util methods to deal with string arrays. environment and process arguments 529 * are both represented as string arrays. */ 530 531 static char const **bear_strings_build(char const *const arg, va_list *args) { 532 char const **result = 0; 533 size_t size = 0; 534 for (char const *it = arg; it; it = va_arg(*args, char const *)) { 535 result = realloc(result, (size + 1) * sizeof(char const *)); 536 if (0 == result) { 537 perror("bear: realloc"); 538 exit(EXIT_FAILURE); 539 } 540 char const *copy = strdup(it); 541 if (0 == copy) { 542 perror("bear: strdup"); 543 exit(EXIT_FAILURE); 544 } 545 result[size++] = copy; 546 } 547 result = realloc(result, (size + 1) * sizeof(char const *)); 548 if (0 == result) { 549 perror("bear: realloc"); 550 exit(EXIT_FAILURE); 551 } 552 result[size++] = 0; 553 554 return result; 555 } 556 557 static char const **bear_strings_copy(char const **const in) { 558 size_t const size = bear_strings_length(in); 559 560 char const **const result = malloc((size + 1) * sizeof(char const *)); 561 if (0 == result) { 562 perror("bear: malloc"); 563 exit(EXIT_FAILURE); 564 } 565 566 char const **out_it = result; 567 for (char const *const *in_it = in; (in_it) && (*in_it); 568 ++in_it, ++out_it) { 569 *out_it = strdup(*in_it); 570 if (0 == *out_it) { 571 perror("bear: strdup"); 572 exit(EXIT_FAILURE); 573 } 574 } 575 *out_it = 0; 576 return result; 577 } 578 579 static char const **bear_strings_append(char const **const in, 580 char const *const e) { 581 size_t size = bear_strings_length(in); 582 char const **result = realloc(in, (size + 2) * sizeof(char const *)); 583 if (0 == result) { 584 perror("bear: realloc"); 585 exit(EXIT_FAILURE); 586 } 587 result[size++] = e; 588 result[size++] = 0; 589 return result; 590 } 591 592 static size_t bear_strings_length(char const *const *const in) { 593 size_t result = 0; 594 for (char const *const *it = in; (it) && (*it); ++it) 595 ++result; 596 return result; 597 } 598 599 static void bear_strings_release(char const **in) { 600 for (char const *const *it = in; (it) && (*it); ++it) { 601 free((void *)*it); 602 } 603 free((void *)in); 604 } 605