brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.2 KiB · 3232511 Raw
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(&current_env);317  bear_reset_env_t(&initial_env);318  int const result = (*fp)(file, argv);319  bear_reset_env_t(&current_env);320  bear_release_env_t(&current_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(&current_env);335  bear_reset_env_t(&initial_env);336  int const result = (*fp)(file, search_path, argv);337  bear_reset_env_t(&current_env);338  bear_release_env_t(&current_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