1 /* -*- coding: utf-8 -*- 2 // The LLVM Compiler Infrastructure 3 // 4 // This file is distributed under the University of Illinois Open Source 5 // License. See LICENSE.TXT for details. 6 */ 7 8 /** 9 * This file implements a shared library. This library can be pre-loaded by 10 * the dynamic linker of the Operating System (OS). It implements a few function 11 * related to process creation. By pre-load this library the executed process 12 * uses these functions instead of those from the standard library. 13 * 14 * The idea here is to inject a logic before call the real methods. The logic is 15 * to dump the call into a file. To call the real method this library is doing 16 * the job of the dynamic linker. 17 * 18 * The only input for the log writing is about the destination directory. 19 * This is passed as environment variable. 20 */ 21 22 #include "config.h" 23 24 #include <stddef.h> 25 #include <stdarg.h> 26 #include <stdlib.h> 27 #include <stdio.h> 28 #include <string.h> 29 #include <unistd.h> 30 #include <dlfcn.h> 31 #include <pthread.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 65 typedef char const * bear_env_t[ENV_SIZE]; 66 67 static int bear_capture_env_t(bear_env_t *env); 68 static int bear_reset_env_t(bear_env_t *env); 69 static void bear_release_env_t(bear_env_t *env); 70 static char const **bear_update_environment(char *const envp[], bear_env_t *env); 71 static char const **bear_update_environ(char const **in, char const *key, char const *value); 72 static char **bear_get_environment(); 73 static void bear_report_call(char const *fun, char const *const argv[]); 74 static char const **bear_strings_build(char const *arg, va_list *ap); 75 static char const **bear_strings_copy(char const **const in); 76 static char const **bear_strings_append(char const **in, char const *e); 77 static size_t bear_strings_length(char const *const *in); 78 static void bear_strings_release(char const **); 79 80 81 static bear_env_t env_names = 82 { ENV_OUTPUT 83 , ENV_PRELOAD 84 #ifdef ENV_FLAT 85 , ENV_FLAT 86 #endif 87 }; 88 89 static bear_env_t initial_env = 90 { 0 91 , 0 92 #ifdef ENV_FLAT 93 , 0 94 #endif 95 }; 96 97 static int initialized = 0; 98 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 99 100 static void on_load(void) __attribute__((constructor)); 101 static void on_unload(void) __attribute__((destructor)); 102 103 104 #ifdef HAVE_EXECVE 105 static int call_execve(const char *path, char *const argv[], 106 char *const envp[]); 107 #endif 108 #ifdef HAVE_EXECVP 109 static int call_execvp(const char *file, char *const argv[]); 110 #endif 111 #ifdef HAVE_EXECVPE 112 static int call_execvpe(const char *file, char *const argv[], 113 char *const envp[]); 114 #endif 115 #ifdef HAVE_EXECVP2 116 static int call_execvP(const char *file, const char *search_path, 117 char *const argv[]); 118 #endif 119 #ifdef HAVE_EXECT 120 static int call_exect(const char *path, char *const argv[], 121 char *const envp[]); 122 #endif 123 #ifdef HAVE_POSIX_SPAWN 124 static int call_posix_spawn(pid_t *restrict pid, const char *restrict path, 125 const posix_spawn_file_actions_t *file_actions, 126 const posix_spawnattr_t *restrict attrp, 127 char *const argv[restrict], 128 char *const envp[restrict]); 129 #endif 130 #ifdef HAVE_POSIX_SPAWNP 131 static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file, 132 const posix_spawn_file_actions_t *file_actions, 133 const posix_spawnattr_t *restrict attrp, 134 char *const argv[restrict], 135 char *const envp[restrict]); 136 #endif 137 138 139 /* Initialization method to Captures the relevant environment variables. 140 */ 141 142 static void on_load(void) { 143 pthread_mutex_lock(&mutex); 144 if (!initialized) 145 initialized = bear_capture_env_t(&initial_env); 146 pthread_mutex_unlock(&mutex); 147 } 148 149 static void on_unload(void) { 150 pthread_mutex_lock(&mutex); 151 bear_release_env_t(&initial_env); 152 initialized = 0; 153 pthread_mutex_unlock(&mutex); 154 } 155 156 157 /* These are the methods we are try to hijack. 158 */ 159 160 #ifdef HAVE_EXECVE 161 int execve(const char *path, char *const argv[], char *const envp[]) { 162 bear_report_call(__func__, (char const *const *)argv); 163 return call_execve(path, argv, envp); 164 } 165 #endif 166 167 #ifdef HAVE_EXECV 168 #ifndef HAVE_EXECVE 169 #error can not implement execv without execve 170 #endif 171 int execv(const char *path, char *const argv[]) { 172 bear_report_call(__func__, (char const *const *)argv); 173 char * const * envp = bear_get_environment(); 174 return call_execve(path, argv, envp); 175 } 176 #endif 177 178 #ifdef HAVE_EXECVPE 179 int execvpe(const char *file, char *const argv[], char *const envp[]) { 180 bear_report_call(__func__, (char const *const *)argv); 181 return call_execvpe(file, argv, envp); 182 } 183 #endif 184 185 #ifdef HAVE_EXECVP 186 int execvp(const char *file, char *const argv[]) { 187 bear_report_call(__func__, (char const *const *)argv); 188 return call_execvp(file, argv); 189 } 190 #endif 191 192 #ifdef HAVE_EXECVP2 193 int execvP(const char *file, const char *search_path, char *const argv[]) { 194 bear_report_call(__func__, (char const *const *)argv); 195 return call_execvP(file, search_path, argv); 196 } 197 #endif 198 199 #ifdef HAVE_EXECT 200 int exect(const char *path, char *const argv[], char *const envp[]) { 201 bear_report_call(__func__, (char const *const *)argv); 202 return call_exect(path, argv, envp); 203 } 204 #endif 205 206 #ifdef HAVE_EXECL 207 # ifndef HAVE_EXECVE 208 # error can not implement execl without execve 209 # endif 210 int execl(const char *path, const char *arg, ...) { 211 va_list args; 212 va_start(args, arg); 213 char const **argv = bear_strings_build(arg, &args); 214 va_end(args); 215 216 bear_report_call(__func__, (char const *const *)argv); 217 char * const * envp = bear_get_environment(); 218 int const result = call_execve(path, (char *const *)argv, envp); 219 220 bear_strings_release(argv); 221 return result; 222 } 223 #endif 224 225 #ifdef HAVE_EXECLP 226 # ifndef HAVE_EXECVP 227 # error can not implement execlp without execvp 228 # endif 229 int execlp(const char *file, const char *arg, ...) { 230 va_list args; 231 va_start(args, arg); 232 char const **argv = bear_strings_build(arg, &args); 233 va_end(args); 234 235 bear_report_call(__func__, (char const *const *)argv); 236 int const result = call_execvp(file, (char *const *)argv); 237 238 bear_strings_release(argv); 239 return result; 240 } 241 #endif 242 243 #ifdef HAVE_EXECLE 244 # ifndef HAVE_EXECVE 245 # error can not implement execle without execve 246 # endif 247 // int execle(const char *path, const char *arg, ..., char * const envp[]); 248 int execle(const char *path, const char *arg, ...) { 249 va_list args; 250 va_start(args, arg); 251 char const **argv = bear_strings_build(arg, &args); 252 char const **envp = va_arg(args, char const **); 253 va_end(args); 254 255 bear_report_call(__func__, (char const *const *)argv); 256 int const result = 257 call_execve(path, (char *const *)argv, (char *const *)envp); 258 259 bear_strings_release(argv); 260 return result; 261 } 262 #endif 263 264 #ifdef HAVE_POSIX_SPAWN 265 int posix_spawn(pid_t *restrict pid, const char *restrict path, 266 const posix_spawn_file_actions_t *file_actions, 267 const posix_spawnattr_t *restrict attrp, 268 char *const argv[restrict], char *const envp[restrict]) { 269 bear_report_call(__func__, (char const *const *)argv); 270 return call_posix_spawn(pid, path, file_actions, attrp, argv, envp); 271 } 272 #endif 273 274 #ifdef HAVE_POSIX_SPAWNP 275 int posix_spawnp(pid_t *restrict pid, const char *restrict file, 276 const posix_spawn_file_actions_t *file_actions, 277 const posix_spawnattr_t *restrict attrp, 278 char *const argv[restrict], char *const envp[restrict]) { 279 bear_report_call(__func__, (char const *const *)argv); 280 return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp); 281 } 282 #endif 283 284 /* These are the methods which forward the call to the standard implementation. 285 */ 286 287 #ifdef HAVE_EXECVE 288 static int call_execve(const char *path, char *const argv[], 289 char *const envp[]) { 290 typedef int (*func)(const char *, char *const *, char *const *); 291 292 DLSYM(func, fp, "execve"); 293 294 char const **const menvp = bear_update_environment(envp, &initial_env); 295 int const result = (*fp)(path, argv, (char *const *)menvp); 296 bear_strings_release(menvp); 297 return result; 298 } 299 #endif 300 301 #ifdef HAVE_EXECVPE 302 static int call_execvpe(const char *file, char *const argv[], 303 char *const envp[]) { 304 typedef int (*func)(const char *, char *const *, char *const *); 305 306 DLSYM(func, fp, "execvpe"); 307 308 char const **const menvp = bear_update_environment(envp, &initial_env); 309 int const result = (*fp)(file, argv, (char *const *)menvp); 310 bear_strings_release(menvp); 311 return result; 312 } 313 #endif 314 315 #ifdef HAVE_EXECVP 316 static int call_execvp(const char *file, char *const argv[]) { 317 typedef int (*func)(const char *file, char *const argv[]); 318 319 DLSYM(func, fp, "execvp"); 320 321 bear_env_t current_env; 322 bear_capture_env_t(¤t_env); 323 bear_reset_env_t(&initial_env); 324 int const result = (*fp)(file, argv); 325 bear_reset_env_t(¤t_env); 326 bear_release_env_t(¤t_env); 327 328 return result; 329 } 330 #endif 331 332 #ifdef HAVE_EXECVP2 333 static int call_execvP(const char *file, const char *search_path, 334 char *const argv[]) { 335 typedef int (*func)(const char *, const char *, char *const *); 336 337 DLSYM(func, fp, "execvP"); 338 339 bear_env_t current_env; 340 bear_capture_env_t(¤t_env); 341 bear_reset_env_t(&initial_env); 342 int const result = (*fp)(file, search_path, argv); 343 bear_reset_env_t(¤t_env); 344 bear_release_env_t(¤t_env); 345 346 return result; 347 } 348 #endif 349 350 #ifdef HAVE_EXECT 351 static int call_exect(const char *path, char *const argv[], 352 char *const envp[]) { 353 typedef int (*func)(const char *, char *const *, char *const *); 354 355 DLSYM(func, fp, "exect"); 356 357 char const **const menvp = bear_update_environment(envp, &initial_env); 358 int const result = (*fp)(path, argv, (char *const *)menvp); 359 bear_strings_release(menvp); 360 return result; 361 } 362 #endif 363 364 #ifdef HAVE_POSIX_SPAWN 365 static int call_posix_spawn(pid_t *restrict pid, const char *restrict path, 366 const posix_spawn_file_actions_t *file_actions, 367 const posix_spawnattr_t *restrict attrp, 368 char *const argv[restrict], 369 char *const envp[restrict]) { 370 typedef int (*func)(pid_t *restrict, const char *restrict, 371 const posix_spawn_file_actions_t *, 372 const posix_spawnattr_t *restrict, 373 char *const *restrict, char *const *restrict); 374 375 DLSYM(func, fp, "posix_spawn"); 376 377 char const **const menvp = bear_update_environment(envp, &initial_env); 378 int const result = 379 (*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp); 380 bear_strings_release(menvp); 381 return result; 382 } 383 #endif 384 385 #ifdef HAVE_POSIX_SPAWNP 386 static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file, 387 const posix_spawn_file_actions_t *file_actions, 388 const posix_spawnattr_t *restrict attrp, 389 char *const argv[restrict], 390 char *const envp[restrict]) { 391 typedef int (*func)(pid_t *restrict, const char *restrict, 392 const posix_spawn_file_actions_t *, 393 const posix_spawnattr_t *restrict, 394 char *const *restrict, char *const *restrict); 395 396 DLSYM(func, fp, "posix_spawnp"); 397 398 char const **const menvp = bear_update_environment(envp, &initial_env); 399 int const result = 400 (*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp); 401 bear_strings_release(menvp); 402 return result; 403 } 404 #endif 405 406 /* this method is to write log about the process creation. */ 407 408 static void bear_report_call(char const *fun, char const *const argv[]) { 409 static int const GS = 0x1d; 410 static int const RS = 0x1e; 411 static int const US = 0x1f; 412 413 if (!initialized) 414 return; 415 416 pthread_mutex_lock(&mutex); 417 const char *cwd = getcwd(NULL, 0); 418 if (0 == cwd) { 419 perror("bear: getcwd"); 420 exit(EXIT_FAILURE); 421 } 422 char const * const out_dir = initial_env[0]; 423 size_t const path_max_length = strlen(out_dir) + 32; 424 char filename[path_max_length]; 425 if (-1 == snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) { 426 perror("bear: snprintf"); 427 exit(EXIT_FAILURE); 428 } 429 FILE * fd = fopen(filename, "a+"); 430 if (0 == fd) { 431 perror("bear: fopen"); 432 exit(EXIT_FAILURE); 433 } 434 fprintf(fd, "%d%c", getpid(), RS); 435 fprintf(fd, "%d%c", getppid(), RS); 436 fprintf(fd, "%s%c", fun, RS); 437 fprintf(fd, "%s%c", cwd, RS); 438 size_t const argc = bear_strings_length(argv); 439 for (size_t it = 0; it < argc; ++it) { 440 fprintf(fd, "%s%c", argv[it], US); 441 } 442 fprintf(fd, "%c", GS); 443 if (fclose(fd)) { 444 perror("bear: fclose"); 445 exit(EXIT_FAILURE); 446 } 447 free((void *)cwd); 448 pthread_mutex_unlock(&mutex); 449 } 450 451 /* update environment assure that chilren processes will copy the desired 452 * behaviour */ 453 454 static int bear_capture_env_t(bear_env_t *env) { 455 int status = 1; 456 for (size_t it = 0; it < ENV_SIZE; ++it) { 457 char const * const env_value = getenv(env_names[it]); 458 char const * const env_copy = (env_value) ? strdup(env_value) : env_value; 459 (*env)[it] = env_copy; 460 status &= (env_copy) ? 1 : 0; 461 } 462 return status; 463 } 464 465 static int bear_reset_env_t(bear_env_t *env) { 466 int status = 1; 467 for (size_t it = 0; it < ENV_SIZE; ++it) { 468 if ((*env)[it]) { 469 setenv(env_names[it], (*env)[it], 1); 470 } else { 471 unsetenv(env_names[it]); 472 } 473 } 474 return status; 475 } 476 477 static void bear_release_env_t(bear_env_t *env) { 478 for (size_t it = 0; it < ENV_SIZE; ++it) { 479 free((void *)(*env)[it]); 480 (*env)[it] = 0; 481 } 482 } 483 484 static char const **bear_update_environment(char *const envp[], 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, char const * const value) { 492 // find the key if it's there 493 size_t const key_length = strlen(key); 494 char const **it = envs; 495 for (; (it) && (*it); ++it) { 496 if ((0 == strncmp(*it, key, key_length)) && 497 (strlen(*it) > key_length) && ('=' == (*it)[key_length])) 498 break; 499 } 500 // allocate a environment entry 501 size_t const value_length = strlen(value); 502 size_t const env_length = key_length + value_length + 2; 503 char *env = malloc(env_length); 504 if (0 == env) { 505 perror("bear: malloc [in env_update]"); 506 exit(EXIT_FAILURE); 507 } 508 if (-1 == snprintf(env, env_length, "%s=%s", key, value)) { 509 perror("bear: snprintf"); 510 exit(EXIT_FAILURE); 511 } 512 // replace or append the environment entry 513 if (it && *it) { 514 free((void *)*it); 515 *it = env; 516 return envs; 517 } 518 return bear_strings_append(envs, env); 519 } 520 521 static char **bear_get_environment() { 522 #if defined HAVE_NSGETENVIRON 523 return *_NSGetEnviron(); 524 #else 525 return environ; 526 #endif 527 } 528 529 /* util methods to deal with string arrays. environment and process arguments 530 * are both represented as string arrays. */ 531 532 static char const **bear_strings_build(char const *const arg, va_list *args) { 533 char const **result = 0; 534 size_t size = 0; 535 for (char const *it = arg; it; it = va_arg(*args, char const *)) { 536 result = realloc(result, (size + 1) * sizeof(char const *)); 537 if (0 == result) { 538 perror("bear: realloc"); 539 exit(EXIT_FAILURE); 540 } 541 char const *copy = strdup(it); 542 if (0 == copy) { 543 perror("bear: strdup"); 544 exit(EXIT_FAILURE); 545 } 546 result[size++] = copy; 547 } 548 result = realloc(result, (size + 1) * sizeof(char const *)); 549 if (0 == result) { 550 perror("bear: realloc"); 551 exit(EXIT_FAILURE); 552 } 553 result[size++] = 0; 554 555 return result; 556 } 557 558 static char const **bear_strings_copy(char const **const in) { 559 size_t const size = bear_strings_length(in); 560 561 char const **const result = malloc((size + 1) * sizeof(char const *)); 562 if (0 == result) { 563 perror("bear: malloc"); 564 exit(EXIT_FAILURE); 565 } 566 567 char const **out_it = result; 568 for (char const *const *in_it = in; (in_it) && (*in_it); 569 ++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