606 lines · c
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-exception5*/6 7/**8 * This file implements a shared library. This library can be pre-loaded by9 * the dynamic linker of the Operating System (OS). It implements a few function10 * related to process creation. By pre-load this library the executed process11 * 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 is14 * to dump the call into a file. To call the real method this library is doing15 * 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// NOLINTNEXTLINE22#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_SPAWNP34#include <spawn.h>35#endif36 37#if defined HAVE_NSGETENVIRON38#include <crt_externs.h>39#else40extern char **environ;41#endif42 43#define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR"44#ifdef APPLE45#define ENV_FLAT "DYLD_FORCE_FLAT_NAMESPACE"46#define ENV_PRELOAD "DYLD_INSERT_LIBRARIES"47#define ENV_SIZE 348#else49#define ENV_PRELOAD "LD_PRELOAD"50#define ENV_SIZE 251#endif52 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_PRELOAD82#ifdef ENV_FLAT83 ,84 ENV_FLAT85#endif86};87 88static bear_env_t initial_env = {0, 089#ifdef ENV_FLAT90 ,91 092#endif93};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_EXECVE102static int call_execve(const char *path, char *const argv[],103 char *const envp[]);104#endif105#ifdef HAVE_EXECVP106static int call_execvp(const char *file, char *const argv[]);107#endif108#ifdef HAVE_EXECVPE109static int call_execvpe(const char *file, char *const argv[],110 char *const envp[]);111#endif112#ifdef HAVE_EXECVP2113static int call_execvP(const char *file, const char *search_path,114 char *const argv[]);115#endif116#ifdef HAVE_EXECT117static int call_exect(const char *path, char *const argv[], char *const envp[]);118#endif119#ifdef HAVE_POSIX_SPAWN120static 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#endif126#ifdef HAVE_POSIX_SPAWNP127static 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#endif133 134/* Initialization method to Captures the relevant environment variables.135 */136 137static 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 144static 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_EXECVE155int 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#endif160 161#ifdef HAVE_EXECV162#ifndef HAVE_EXECVE163#error can not implement execv without execve164#endif165int 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#endif171 172#ifdef HAVE_EXECVPE173int 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#endif178 179#ifdef HAVE_EXECVP180int execvp(const char *file, char *const argv[]) {181 bear_report_call(__func__, (char const *const *)argv);182 return call_execvp(file, argv);183}184#endif185 186#ifdef HAVE_EXECVP2187int 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#endif192 193#ifdef HAVE_EXECT194int 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#endif199 200#ifdef HAVE_EXECL201#ifndef HAVE_EXECVE202#error can not implement execl without execve203#endif204int 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#endif218 219#ifdef HAVE_EXECLP220#ifndef HAVE_EXECVP221#error can not implement execlp without execvp222#endif223int 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#endif236 237#ifdef HAVE_EXECLE238#ifndef HAVE_EXECVE239#error can not implement execle without execve240#endif241// 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#endif257 258#ifdef HAVE_POSIX_SPAWN259int 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#endif267 268#ifdef HAVE_POSIX_SPAWNP269int 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#endif277 278/* These are the methods which forward the call to the standard implementation.279 */280 281#ifdef HAVE_EXECVE282static 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#endif294 295#ifdef HAVE_EXECVPE296static 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#endif308 309#ifdef HAVE_EXECVP310static 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#endif325 326#ifdef HAVE_EXECVP2327static 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#endif343 344#ifdef HAVE_EXECT345static 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#endif357 358#ifdef HAVE_POSIX_SPAWN359static 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#endif378 379#ifdef HAVE_POSIX_SPAWNP380static 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#endif399 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);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 desired451 * 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(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 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(env_names[it], (*env)[it], 1);469 } else {470 unsetenv(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((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((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 491static char const **bear_update_environ(char const *envs[], char const *key,492 char const *const value) {493 // find the key if it's there494 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 entry502 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 entry514 if (it && *it) {515 free((void *)*it);516 *it = env;517 return envs;518 }519 return bear_strings_append(envs, env);520}521 522static char **bear_get_environment() {523#if defined HAVE_NSGETENVIRON524 return *_NSGetEnviron();525#else526 return environ;527#endif528}529 530/* util methods to deal with string arrays. environment and process arguments531 * 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(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 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 + 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 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(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 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((void *)*it);603 }604 free((void *)in);605}606