brintos

brintos / llvm-project-archived public Read only

0
0
Text · 29.8 KiB · de0be33 Raw
933 lines · cpp
1//===-- FDInterposing.cpp ---------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file helps with catching double close calls on unix integer file10// descriptors by interposing functions for all file descriptor create and11// close operations. A stack backtrace for every create and close function is12// maintained, and every create and close operation is logged. When a double13// file descriptor close is encountered, it will be logged.14//15// To enable the interposing in a darwin program, set the DYLD_INSERT_LIBRARIES16// environment variable as follows:17// For sh:18//  DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib /path/to/executable19// For tcsh:20//  (setenv DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib ;21//  /path/to/executable)22//23// Other environment variables that can alter the default actions of this24// interposing shared library include:25//26// "FileDescriptorStackLoggingNoCompact"27//28//      With this environment variable set, all file descriptor create and29//      delete operations will be permanantly maintained in the event map.30//      The default action is to compact the create/delete events by removing31//      any previous file descriptor create events that are matched with a32//      corresponding file descriptor delete event when the next valid file33//      descriptor create event is detected.34//35// "FileDescriptorMinimalLogging"36//37//      By default every file descriptor create and delete operation is logged38//      (to STDOUT by default, see the "FileDescriptorLogFile"). This can be39//      suppressed to only show errors and warnings by setting this environment40//      variable (the value in not important).41//42// "FileDescriptorLogFile=<path>"43//44//      By default logging goes to STDOUT_FILENO, but this can be changed by45//      setting FileDescriptorLogFile. The value is a path to a file that46//      will be opened and used for logging.47//===----------------------------------------------------------------------===//48 49#include <assert.h>50#include <dirent.h>51#include <errno.h>52#include <execinfo.h>53#include <fcntl.h>54#include <libgen.h>55#include <mach-o/dyld-interposing.h>56#include <mach-o/dyld.h>57#include <map>58#include <stdio.h>59#include <stdlib.h>60#include <string.h>61#include <string>62#include <sys/event.h>63#include <sys/mman.h>64#include <sys/socket.h>65#include <sys/time.h>66#include <sys/types.h>67#include <tr1/memory>68#include <unistd.h>69#include <vector>70 71extern "C" {72int accept$NOCANCEL(int, struct sockaddr *__restrict, socklen_t *__restrict);73int close$NOCANCEL(int);74int open$NOCANCEL(const char *, int, ...);75int __open_extended(const char *, int, uid_t, gid_t, int,76                    struct kauth_filesec *);77}78 79namespace fd_interposing {80 81// String class so we can get formatted strings without having to worry82// about the memory storage since it will allocate the memory it needs.83class String {84public:85  String() : m_str(NULL) {}86 87  String(const char *format, ...) : m_str(NULL) {88    va_list args;89    va_start(args, format);90    vprintf(format, args);91    va_end(args);92  }93 94  ~String() { reset(); }95 96  void reset(char *s = NULL) {97    if (m_str)98      ::free(m_str);99    m_str = s;100  }101 102  const char *c_str() const { return m_str; }103 104  void printf(const char *format, ...) {105    va_list args;106    va_start(args, format);107    vprintf(format, args);108    va_end(args);109  }110  void vprintf(const char *format, va_list args) {111    reset();112    ::vasprintf(&m_str, format, args);113  }114 115  void log(int log_fd) {116    if (m_str && log_fd >= 0) {117      const int len = strlen(m_str);118      if (len > 0) {119        write(log_fd, m_str, len);120        const char last_char = m_str[len - 1];121        if (!(last_char == '\n' || last_char == '\r'))122          write(log_fd, "\n", 1);123      }124    }125  }126 127protected:128  char *m_str;129 130private:131  String(const String &) = delete;132  const String &operator=(const String &) = delete;133};134 135// Type definitions136typedef std::vector<void *> Frames;137class FDEvent;138typedef std::vector<void *> Frames;139typedef std::tr1::shared_ptr<FDEvent> FDEventSP;140typedef std::tr1::shared_ptr<String> StringSP;141 142// FDEvent143//144// A class that describes a file descriptor event.145//146// File descriptor events fall into one of two categories: create events147// and delete events.148class FDEvent {149public:150  FDEvent(int fd, int err, const StringSP &string_sp, bool is_create,151          const Frames &frames)152      : m_string_sp(string_sp), m_frames(frames.begin(), frames.end()),153        m_fd(fd), m_err(err), m_is_create(is_create) {}154 155  ~FDEvent() {}156 157  bool IsCreateEvent() const { return m_is_create; }158 159  bool IsDeleteEvent() const { return !m_is_create; }160 161  Frames &GetFrames() { return m_frames; }162 163  const Frames &GetFrames() const { return m_frames; }164 165  int GetFD() const { return m_fd; }166 167  int GetError() const { return m_err; }168 169  void Dump(int log_fd) const;170 171  void SetCreateEvent(FDEventSP &create_event_sp) {172    m_create_event_sp = create_event_sp;173  }174 175private:176  // A shared pointer to a String that describes this event in177  // detail (all args and return and error values)178  StringSP m_string_sp;179  // The frames for the stack backtrace for this event180  Frames m_frames;181  // If this is a file descriptor delete event, this might contain182  // the corresponding file descriptor create event183  FDEventSP m_create_event_sp;184  // The file descriptor for this event185  int m_fd;186  // The error code (if any) for this event187  int m_err;188  // True if this event is a file descriptor create event, false189  // if it is a file descriptor delete event190  bool m_is_create;191};192 193// Templatized class that will save errno only if the "value" it is194// constructed with is equal to INVALID. When the class goes out of195// scope, it will restore errno if it was saved.196template <int INVALID> class Errno {197public:198  // Save errno only if we are supposed to199  Errno(int value)200      : m_saved_errno((value == INVALID) ? errno : 0),201        m_restore(value == INVALID) {}202 203  // Restore errno only if we are supposed to204  ~Errno() {205    if (m_restore)206      errno = m_saved_errno;207  }208 209  // Accessor for the saved value of errno210  int get_errno() const { return m_saved_errno; }211 212protected:213  const int m_saved_errno;214  const bool m_restore;215};216 217typedef Errno<-1> InvalidFDErrno;218typedef Errno<-1> NegativeErrorErrno;219typedef std::vector<FDEventSP> FDEventArray;220typedef std::map<int, FDEventArray> FDEventMap;221 222// Globals223// Global event map that contains all file descriptor events. As file224// descriptor create and close events come in, they will get filled225// into this map (protected by g_mutex). When a file descriptor close226// event is detected, the open event will be removed and placed into227// the close event so if something tries to double close a file228// descriptor we can show the previous close event and the file229// descriptor event that created it. When a new file descriptor create230// event comes in, we will remove the previous one for that file231// descriptor unless the environment variable232// "FileDescriptorStackLoggingNoCompact"233// is set. The file descriptor history can be accessed using the234// get_fd_history() function.235static FDEventMap g_fd_event_map;236// A mutex to protect access to our data structures in g_fd_event_map237// and also our logging messages238static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;239// Log all file descriptor create and close events by default. Only log240// warnings and errors if the "FileDescriptorMinimalLogging" environment241// variable is set.242static int g_log_all_calls = 1;243// We compact the file descriptor events by default. Set the environment244// varible "FileDescriptorStackLoggingNoCompact" to keep a full history.245static int g_compact = 1;246// The current process ID247static int g_pid = -1;248static bool g_enabled = true;249// Mutex class that will lock a mutex when it is constructed, and unlock250// it when is goes out of scope251class Locker {252public:253  Locker(pthread_mutex_t *mutex_ptr) : m_mutex_ptr(mutex_ptr) {254    ::pthread_mutex_lock(m_mutex_ptr);255  }256 257  // This allows clients to test try and acquire the mutex...258  Locker(pthread_mutex_t *mutex_ptr, bool &lock_acquired) : m_mutex_ptr(NULL) {259    lock_acquired = ::pthread_mutex_trylock(mutex_ptr) == 0;260    if (lock_acquired)261      m_mutex_ptr = mutex_ptr;262  }263 264  ~Locker() {265    if (m_mutex_ptr)266      ::pthread_mutex_unlock(m_mutex_ptr);267  }268 269protected:270  pthread_mutex_t *m_mutex_ptr;271};272 273static void log(const char *format, ...) __attribute__((format(printf, 1, 2)));274 275static void log(int log_fd, const FDEvent *event, const char *format, ...)276    __attribute__((format(printf, 3, 4)));277 278static void backtrace_log(const char *format, ...)279    __attribute__((format(printf, 1, 2)));280 281static void backtrace_error(const char *format, ...)282    __attribute__((format(printf, 1, 2)));283 284static void log_to_fd(int log_fd, const char *format, ...)285    __attribute__((format(printf, 2, 3)));286 287static inline size_t get_backtrace(Frames &frame_buffer,288                                   size_t frames_to_remove) {289  void *frames[2048];290  int count = ::backtrace(&frames[0], sizeof(frames) / sizeof(void *));291  if (count > frames_to_remove)292    frame_buffer.assign(&frames[frames_to_remove], &frames[count]);293  else294    frame_buffer.assign(&frames[0], &frames[count]);295  while (frame_buffer.back() < (void *)1024)296    frame_buffer.pop_back();297  return frame_buffer.size();298}299 300static int g_log_fd = STDOUT_FILENO;301static int g_initialized = 0;302 303const char *get_process_fullpath(bool force = false) {304  static char g_process_fullpath[PATH_MAX] = {0};305  if (force || g_process_fullpath[0] == '\0') {306    // If DST is NULL, then return the number of bytes needed.307    uint32_t len = sizeof(g_process_fullpath);308    if (_NSGetExecutablePath(g_process_fullpath, &len) != 0)309      strncpy(g_process_fullpath, "<error>", sizeof(g_process_fullpath));310  }311  return g_process_fullpath;312}313 314// Returns the current process ID, or -1 if inserposing not enabled for315// this process316static int get_interposed_pid() {317  if (!g_enabled)318    return -1;319 320  const pid_t pid = getpid();321  if (g_pid != pid) {322    if (g_pid == -1) {323      g_pid = pid;324      log("Interposing file descriptor create and delete functions for %s "325          "(pid=%i)\n",326          get_process_fullpath(true), pid);327    } else {328      log("pid=%i: disabling interposing file descriptor create and delete "329          "functions for child process %s (pid=%i)\n",330          g_pid, get_process_fullpath(true), pid);331      g_enabled = false;332      return -1;333    }334    // Log when our process changes335  }336  return g_pid;337}338 339static int get_logging_fd() {340  if (!g_enabled)341    return -1;342 343  if (!g_initialized) {344    g_initialized = 1;345 346    const pid_t pid = get_interposed_pid();347 348    if (g_enabled) {349      // Keep all stack info around for all fd create and delete calls.350      // Otherwise we will remove the fd create call when a corresponding351      // fd delete call is received352      if (getenv("FileDescriptorStackLoggingNoCompact"))353        g_compact = 0;354 355      if (getenv("FileDescriptorMinimalLogging"))356        g_log_all_calls = 0;357 358      const char *log_path = getenv("FileDescriptorLogFile");359      if (log_path)360        g_log_fd = ::creat(log_path, 0660);361      else362        g_log_fd = STDOUT_FILENO;363 364      // Only let this interposing happen on the first time this matches365      // and stop this from happening so any child processes don't also366      // log their file descriptors367      ::unsetenv("DYLD_INSERT_LIBRARIES");368    } else {369      log("pid=%i: logging disabled\n", getpid());370    }371  }372  return g_log_fd;373}374 375void log_to_fd(int log_fd, const char *format, va_list args) {376  if (format && format[0] && log_fd >= 0) {377    char buffer[PATH_MAX];378    const int count = ::vsnprintf(buffer, sizeof(buffer), format, args);379    if (count > 0)380      write(log_fd, buffer, count);381  }382}383 384void log_to_fd(int log_fd, const char *format, ...) {385  if (format && format[0]) {386    va_list args;387    va_start(args, format);388    log_to_fd(log_fd, format, args);389    va_end(args);390  }391}392 393void log(const char *format, va_list args) {394  log_to_fd(get_logging_fd(), format, args);395}396 397void log(const char *format, ...) {398  if (format && format[0]) {399    va_list args;400    va_start(args, format);401    log(format, args);402    va_end(args);403  }404}405 406void log(int log_fd, const FDEvent *event, const char *format, ...) {407  if (format && format[0]) {408    va_list args;409    va_start(args, format);410    log_to_fd(log_fd, format, args);411    va_end(args);412  }413  if (event)414    event->Dump(log_fd);415}416 417void FDEvent::Dump(int log_fd) const {418  if (log_fd >= 0) {419    log_to_fd(log_fd, "%s\n", m_string_sp->c_str());420    if (!m_frames.empty())421      ::backtrace_symbols_fd(m_frames.data(), m_frames.size(), log_fd);422 423    if (m_create_event_sp) {424      log_to_fd(log_fd, "\nfd=%i was created with this event:\n", m_fd);425      m_create_event_sp->Dump(log_fd);426      log_to_fd(log_fd, "\n");427    }428  }429}430 431void backtrace_log(const char *format, ...) {432  const int log_fd = get_logging_fd();433  if (log_fd >= 0) {434    if (format && format[0]) {435      va_list args;436      va_start(args, format);437      log(format, args);438      va_end(args);439    }440 441    Frames frames;442    if (get_backtrace(frames, 2))443      ::backtrace_symbols_fd(frames.data(), frames.size(), log_fd);444  }445}446 447void backtrace_error(const char *format, ...) {448  const int pid = get_interposed_pid();449  if (pid >= 0) {450    const int log_fd = get_logging_fd();451    if (log_fd >= 0) {452      log("\nerror: %s (pid=%i): ", get_process_fullpath(), pid);453 454      if (format && format[0]) {455        va_list args;456        va_start(args, format);457        log(format, args);458        va_end(args);459      }460 461      Frames frames;462      if (get_backtrace(frames, 2))463        ::backtrace_symbols_fd(frames.data(), frames.size(), log_fd);464    }465  }466}467 468void save_backtrace(int fd, int err, const StringSP &string_sp,469                    bool is_create) {470  Frames frames;471  get_backtrace(frames, 2);472 473  FDEventSP fd_event_sp(new FDEvent(fd, err, string_sp, is_create, frames));474 475  FDEventMap::iterator pos = g_fd_event_map.find(fd);476 477  if (pos != g_fd_event_map.end()) {478    // We have history for this fd...479 480    FDEventArray &event_array = g_fd_event_map[fd];481    if (fd_event_sp->IsCreateEvent()) {482      // The current fd event is a function that creates483      // a descriptor, check in case last event was484      // a create event.485      if (event_array.back()->IsCreateEvent()) {486        const int log_fd = get_logging_fd();487        // Two fd create functions in a row, we missed488        // a function that closes a fd...489        log(log_fd, fd_event_sp.get(), "\nwarning: unmatched file descriptor "490                                       "create event fd=%i (we missed a file "491                                       "descriptor close event):\n",492            fd);493      } else if (g_compact) {494        // We are compacting so we remove previous create event495        // when we get the corresponding delete event496        event_array.pop_back();497      }498    } else {499      // The current fd event is a function that deletes500      // a descriptor, check in case last event for this501      // fd was a delete event (double close!)502      if (event_array.back()->IsDeleteEvent()) {503        const int log_fd = get_logging_fd();504        // Two fd delete functions in a row, we must505        // have missed some function that opened a descriptor506        log(log_fd, fd_event_sp.get(), "\nwarning: unmatched file descriptor "507                                       "close event for fd=%d (we missed the "508                                       "file descriptor create event):\n",509            fd);510      } else if (g_compact) {511        // Since this is a close event, we want to remember the open event512        // that this close if for...513        fd_event_sp->SetCreateEvent(event_array.back());514        // We are compacting so we remove previous create event515        // when we get the corresponding delete event516        event_array.pop_back();517      }518    }519 520    event_array.push_back(fd_event_sp);521  } else {522    g_fd_event_map[fd].push_back(fd_event_sp);523  }524}525 526// socket() interpose function527extern "C" int socket$__interposed__(int domain, int type, int protocol) {528  const int pid = get_interposed_pid();529  if (pid >= 0) {530    Locker locker(&g_mutex);531    const int fd = ::socket(domain, type, protocol);532    InvalidFDErrno fd_errno(fd);533    StringSP description_sp(new String);534    if (fd == -1)535      description_sp->printf("pid=%i: socket (domain = %i, type = %i, protocol "536                             "= %i) => fd=%i  errno = %i",537                             pid, domain, type, protocol, fd,538                             fd_errno.get_errno());539    else540      description_sp->printf(541          "pid=%i: socket (domain = %i, type = %i, protocol = %i) => fd=%i",542          pid, domain, type, protocol, fd);543    if (g_log_all_calls)544      description_sp->log(get_logging_fd());545    if (fd >= 0)546      save_backtrace(fd, fd_errno.get_errno(), description_sp, true);547    return fd;548  } else {549    return ::socket(domain, type, protocol);550  }551}552 553// socketpair() interpose function554extern "C" int socketpair$__interposed__(int domain, int type, int protocol,555                                         int fds[2]) {556  const int pid = get_interposed_pid();557  if (pid >= 0) {558    Locker locker(&g_mutex);559    fds[0] = -1;560    fds[1] = -1;561    const int err = socketpair(domain, type, protocol, fds);562    NegativeErrorErrno err_errno(err);563    StringSP description_sp(564        new String("pid=%i: socketpair (domain=%i, type=%i, protocol=%i, "565                   "{fd=%i, fd=%i}) -> err=%i",566                   pid, domain, type, protocol, fds[0], fds[1], err));567    if (g_log_all_calls)568      description_sp->log(get_logging_fd());569    if (fds[0] >= 0)570      save_backtrace(fds[0], err_errno.get_errno(), description_sp, true);571    if (fds[1] >= 0)572      save_backtrace(fds[1], err_errno.get_errno(), description_sp, true);573    return err;574  } else {575    return socketpair(domain, type, protocol, fds);576  }577}578 579// open() interpose function580extern "C" int open$__interposed__(const char *path, int oflag, int mode) {581  const int pid = get_interposed_pid();582  if (pid >= 0) {583    Locker locker(&g_mutex);584    int fd = -2;585    StringSP description_sp(new String);586    if (oflag & O_CREAT) {587      fd = ::open(path, oflag, mode);588      description_sp->printf(589          "pid=%i: open (path = '%s', oflag = %i, mode = %i) -> fd=%i", pid,590          path, oflag, mode, fd);591    } else {592      fd = ::open(path, oflag);593      description_sp->printf("pid=%i: open (path = '%s', oflag = %i) -> fd=%i",594                             pid, path, oflag, fd);595    }596 597    InvalidFDErrno fd_errno(fd);598    if (g_log_all_calls)599      description_sp->log(get_logging_fd());600    if (fd >= 0)601      save_backtrace(fd, fd_errno.get_errno(), description_sp, true);602    return fd;603  } else {604    return ::open(path, oflag, mode);605  }606}607 608// open$NOCANCEL() interpose function609extern "C" int open$NOCANCEL$__interposed__(const char *path, int oflag,610                                            int mode) {611  const int pid = get_interposed_pid();612  if (pid >= 0) {613    Locker locker(&g_mutex);614    const int fd = ::open$NOCANCEL(path, oflag, mode);615    InvalidFDErrno fd_errno(fd);616    StringSP description_sp(new String(617        "pid=%i: open$NOCANCEL (path = '%s', oflag = %i, mode = %i) -> fd=%i",618        pid, path, oflag, mode, fd));619    if (g_log_all_calls)620      description_sp->log(get_logging_fd());621    if (fd >= 0)622      save_backtrace(fd, fd_errno.get_errno(), description_sp, true);623    return fd;624  } else {625    return ::open$NOCANCEL(path, oflag, mode);626  }627}628 629// __open_extended() interpose function630extern "C" int __open_extended$__interposed__(const char *path, int oflag,631                                              uid_t uid, gid_t gid, int mode,632                                              struct kauth_filesec *fsacl) {633  const int pid = get_interposed_pid();634  if (pid >= 0) {635    Locker locker(&g_mutex);636    const int fd = ::__open_extended(path, oflag, uid, gid, mode, fsacl);637    InvalidFDErrno fd_errno(fd);638    StringSP description_sp(639        new String("pid=%i: __open_extended (path='%s', oflag=%i, uid=%i, "640                   "gid=%i, mode=%i, fsacl=%p) -> fd=%i",641                   pid, path, oflag, uid, gid, mode, fsacl, fd));642    if (g_log_all_calls)643      description_sp->log(get_logging_fd());644    if (fd >= 0)645      save_backtrace(fd, fd_errno.get_errno(), description_sp, true);646    return fd;647  } else {648    return ::__open_extended(path, oflag, uid, gid, mode, fsacl);649  }650}651 652// kqueue() interpose function653extern "C" int kqueue$__interposed__(void) {654  const int pid = get_interposed_pid();655  if (pid >= 0) {656    Locker locker(&g_mutex);657    const int fd = ::kqueue();658    InvalidFDErrno fd_errno(fd);659    StringSP description_sp(new String("pid=%i: kqueue () -> fd=%i", pid, fd));660    if (g_log_all_calls)661      description_sp->log(get_logging_fd());662    if (fd >= 0)663      save_backtrace(fd, fd_errno.get_errno(), description_sp, true);664    return fd;665  } else {666    return ::kqueue();667  }668}669 670// shm_open() interpose function671extern "C" int shm_open$__interposed__(const char *path, int oflag, int mode) {672  const int pid = get_interposed_pid();673  if (pid >= 0) {674    Locker locker(&g_mutex);675    const int fd = ::shm_open(path, oflag, mode);676    InvalidFDErrno fd_errno(fd);677    StringSP description_sp(new String(678        "pid=%i: shm_open (path = '%s', oflag = %i, mode = %i) -> fd=%i", pid,679        path, oflag, mode, fd));680    if (g_log_all_calls)681      description_sp->log(get_logging_fd());682    if (fd >= 0)683      save_backtrace(fd, fd_errno.get_errno(), description_sp, true);684    return fd;685  } else {686    return ::shm_open(path, oflag, mode);687  }688}689 690// accept() interpose function691extern "C" int accept$__interposed__(int socket, struct sockaddr *address,692                                     socklen_t *address_len) {693  const int pid = get_interposed_pid();694  if (pid >= 0) {695    Locker locker(&g_mutex);696    const int fd = ::accept(socket, address, address_len);697    InvalidFDErrno fd_errno(fd);698    StringSP description_sp(new String(699        "pid=%i: accept (socket=%i, ...) -> fd=%i", pid, socket, fd));700    if (g_log_all_calls)701      description_sp->log(get_logging_fd());702    if (fd >= 0)703      save_backtrace(fd, fd_errno.get_errno(), description_sp, true);704    return fd;705  } else {706    return ::accept(socket, address, address_len);707  }708}709 710// accept$NOCANCEL() interpose function711extern "C" int accept$NOCANCEL$__interposed__(int socket,712                                              struct sockaddr *address,713                                              socklen_t *address_len) {714  const int pid = get_interposed_pid();715  if (pid >= 0) {716    Locker locker(&g_mutex);717    const int fd = ::accept$NOCANCEL(socket, address, address_len);718    InvalidFDErrno fd_errno(fd);719    StringSP description_sp(new String(720        "pid=%i: accept$NOCANCEL (socket=%i, ...) -> fd=%i", pid, socket, fd));721    if (g_log_all_calls)722      description_sp->log(get_logging_fd());723    if (fd >= 0)724      save_backtrace(fd, fd_errno.get_errno(), description_sp, true);725    return fd;726  } else {727    return ::accept$NOCANCEL(socket, address, address_len);728  }729}730 731// dup() interpose function732extern "C" int dup$__interposed__(int fd2) {733  const int pid = get_interposed_pid();734  if (pid >= 0) {735    Locker locker(&g_mutex);736    const int fd = ::dup(fd2);737    InvalidFDErrno fd_errno(fd);738    StringSP description_sp(739        new String("pid=%i: dup (fd2=%i) -> fd=%i", pid, fd2, fd));740    if (g_log_all_calls)741      description_sp->log(get_logging_fd());742    if (fd >= 0)743      save_backtrace(fd, fd_errno.get_errno(), description_sp, true);744    return fd;745  } else {746    return ::dup(fd2);747  }748}749 750// dup2() interpose function751extern "C" int dup2$__interposed__(int fd1, int fd2) {752  const int pid = get_interposed_pid();753  if (pid >= 0) {754    Locker locker(&g_mutex);755    // If "fd2" is already opened, it will be closed during the756    // dup2 call below, so we need to see if we have fd2 in our757    // open map and treat it as a close(fd2)758    FDEventMap::iterator pos = g_fd_event_map.find(fd2);759    StringSP dup2_close_description_sp(760        new String("pid=%i: dup2 (fd1=%i, fd2=%i) -> will close (fd=%i)", pid,761                   fd1, fd2, fd2));762    if (pos != g_fd_event_map.end() && pos->second.back()->IsCreateEvent())763      save_backtrace(fd2, 0, dup2_close_description_sp, false);764 765    const int fd = ::dup2(fd1, fd2);766    InvalidFDErrno fd_errno(fd);767    StringSP description_sp(new String("pid=%i: dup2 (fd1=%i, fd2=%i) -> fd=%i",768                                       pid, fd1, fd2, fd));769    if (g_log_all_calls)770      description_sp->log(get_logging_fd());771 772    if (fd >= 0)773      save_backtrace(fd, fd_errno.get_errno(), description_sp, true);774    return fd;775  } else {776    return ::dup2(fd1, fd2);777  }778}779 780// close() interpose function781extern "C" int close$__interposed__(int fd) {782  const int pid = get_interposed_pid();783  if (pid >= 0) {784    Locker locker(&g_mutex);785    const int err = close(fd);786    NegativeErrorErrno err_errno(err);787    StringSP description_sp(new String);788    if (err == -1)789      description_sp->printf("pid=%i: close (fd=%i) => %i errno = %i (%s))",790                             pid, fd, err, err_errno.get_errno(),791                             strerror(err_errno.get_errno()));792    else793      description_sp->printf("pid=%i: close (fd=%i) => %i", pid, fd, err);794    if (g_log_all_calls)795      description_sp->log(get_logging_fd());796 797    if (err == 0) {798      if (fd >= 0)799        save_backtrace(fd, err, description_sp, false);800    } else if (err == -1) {801      if (err_errno.get_errno() == EBADF && fd != -1) {802        backtrace_error("close (fd=%d) resulted in EBADF:\n", fd);803 804        FDEventMap::iterator pos = g_fd_event_map.find(fd);805        if (pos != g_fd_event_map.end()) {806          log(get_logging_fd(), pos->second.back().get(),807              "\nfd=%d was previously %s with this event:\n", fd,808              pos->second.back()->IsCreateEvent() ? "opened" : "closed");809        }810      }811    }812    return err;813  } else {814    return close(fd);815  }816}817 818// close$NOCANCEL() interpose function819extern "C" int close$NOCANCEL$__interposed__(int fd) {820  const int pid = get_interposed_pid();821  if (pid >= 0) {822    Locker locker(&g_mutex);823    const int err = close$NOCANCEL(fd);824    NegativeErrorErrno err_errno(err);825    StringSP description_sp(new String);826    if (err == -1)827      description_sp->printf(828          "pid=%i: close$NOCANCEL (fd=%i) => %i errno = %i (%s))", pid, fd, err,829          err_errno.get_errno(), strerror(err_errno.get_errno()));830    else831      description_sp->printf("pid=%i: close$NOCANCEL (fd=%i) => %i", pid, fd,832                             err);833    if (g_log_all_calls)834      description_sp->log(get_logging_fd());835 836    if (err == 0) {837      if (fd >= 0)838        save_backtrace(fd, err, description_sp, false);839    } else if (err == -1) {840      if (err_errno.get_errno() == EBADF && fd != -1) {841        backtrace_error("close$NOCANCEL (fd=%d) resulted in EBADF\n:", fd);842 843        FDEventMap::iterator pos = g_fd_event_map.find(fd);844        if (pos != g_fd_event_map.end()) {845          log(get_logging_fd(), pos->second.back().get(),846              "\nfd=%d was previously %s with this event:\n", fd,847              pos->second.back()->IsCreateEvent() ? "opened" : "closed");848        }849      }850    }851    return err;852  } else {853    return close$NOCANCEL(fd);854  }855}856 857// pipe() interpose function858extern "C" int pipe$__interposed__(int fds[2]) {859  const int pid = get_interposed_pid();860  if (pid >= 0) {861    Locker locker(&g_mutex);862    fds[0] = -1;863    fds[1] = -1;864    const int err = pipe(fds);865    const int saved_errno = errno;866    StringSP description_sp(new String(867        "pid=%i: pipe ({fd=%i, fd=%i}) -> err=%i", pid, fds[0], fds[1], err));868    if (g_log_all_calls)869      description_sp->log(get_logging_fd());870    if (fds[0] >= 0)871      save_backtrace(fds[0], saved_errno, description_sp, true);872    if (fds[1] >= 0)873      save_backtrace(fds[1], saved_errno, description_sp, true);874    errno = saved_errno;875    return err;876  } else {877    return pipe(fds);878  }879}880 881// get_fd_history()882//883// This function allows runtime access to the file descriptor history.884//885// @param[in] log_fd886//      The file descriptor to log to887//888// @param[in] fd889//      The file descriptor whose history should be dumped890extern "C" void get_fd_history(int log_fd, int fd) {891  // "create" below needs to be outside of the mutex locker scope892  if (log_fd >= 0) {893    bool got_lock = false;894    Locker locker(&g_mutex, got_lock);895    if (got_lock) {896      FDEventMap::iterator pos = g_fd_event_map.find(fd);897      log_to_fd(log_fd, "Dumping file descriptor history for fd=%i:\n", fd);898      if (pos != g_fd_event_map.end()) {899        FDEventArray &event_array = g_fd_event_map[fd];900        const size_t num_events = event_array.size();901        for (size_t i = 0; i < num_events; ++i)902          event_array[i]->Dump(log_fd);903      } else {904        log_to_fd(log_fd, "error: no file descriptor events found for fd=%i\n",905                  fd);906      }907    } else {908      log_to_fd(log_fd, "error: fd event mutex is locked...\n");909    }910  }911}912 913// Interposing914// FD creation routines915DYLD_INTERPOSE(accept$__interposed__, accept);916DYLD_INTERPOSE(accept$NOCANCEL$__interposed__, accept$NOCANCEL);917DYLD_INTERPOSE(dup$__interposed__, dup);918DYLD_INTERPOSE(dup2$__interposed__, dup2);919DYLD_INTERPOSE(kqueue$__interposed__, kqueue);920DYLD_INTERPOSE(open$__interposed__, open);921DYLD_INTERPOSE(open$NOCANCEL$__interposed__, open$NOCANCEL);922DYLD_INTERPOSE(__open_extended$__interposed__, __open_extended);923DYLD_INTERPOSE(pipe$__interposed__, pipe);924DYLD_INTERPOSE(shm_open$__interposed__, shm_open);925DYLD_INTERPOSE(socket$__interposed__, socket);926DYLD_INTERPOSE(socketpair$__interposed__, socketpair);927 928// FD deleting routines929DYLD_INTERPOSE(close$__interposed__, close);930DYLD_INTERPOSE(close$NOCANCEL$__interposed__, close$NOCANCEL);931 932} // namespace fd_interposing933