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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file helps with catching double close calls on unix integer file
10 // descriptors by interposing functions for all file descriptor create and
11 // close operations. A stack backtrace for every create and close function is
12 // maintained, and every create and close operation is logged. When a double
13 // file descriptor close is encountered, it will be logged.
14 //
15 // To enable the interposing in a darwin program, set the DYLD_INSERT_LIBRARIES
16 // environment variable as follows:
17 // For sh:
18 //  DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib /path/to/executable
19 // 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 this
24 // interposing shared library include:
25 //
26 // "FileDescriptorStackLoggingNoCompact"
27 //
28 //      With this environment variable set, all file descriptor create and
29 //      delete operations will be permanantly maintained in the event map.
30 //      The default action is to compact the create/delete events by removing
31 //      any previous file descriptor create events that are matched with a
32 //      corresponding file descriptor delete event when the next valid file
33 //      descriptor create event is detected.
34 //
35 // "FileDescriptorMinimalLogging"
36 //
37 //      By default every file descriptor create and delete operation is logged
38 //      (to STDOUT by default, see the "FileDescriptorLogFile"). This can be
39 //      suppressed to only show errors and warnings by setting this environment
40 //      variable (the value in not important).
41 //
42 // "FileDescriptorLogFile=<path>"
43 //
44 //      By default logging goes to STDOUT_FILENO, but this can be changed by
45 //      setting FileDescriptorLogFile. The value is a path to a file that
46 //      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 
71 //----------------------------------------------------------------------
72 /// \def DISALLOW_COPY_AND_ASSIGN(TypeName)
73 ///     Macro definition for easily disallowing copy constructor and
74 ///     assignment operators in C++ classes.
75 //----------------------------------------------------------------------
76 #define DISALLOW_COPY_AND_ASSIGN(TypeName)                                     \
77   TypeName(const TypeName &);                                                  \
78   const TypeName &operator=(const TypeName &)
79 
80 extern "C" {
81 int accept$NOCANCEL(int, struct sockaddr *__restrict, socklen_t *__restrict);
82 int close$NOCANCEL(int);
83 int open$NOCANCEL(const char *, int, ...);
84 int __open_extended(const char *, int, uid_t, gid_t, int,
85                     struct kauth_filesec *);
86 }
87 
88 namespace fd_interposing {
89 
90 //----------------------------------------------------------------------
91 // String class so we can get formatted strings without having to worry
92 // about the memory storage since it will allocate the memory it needs.
93 //----------------------------------------------------------------------
94 class String {
95 public:
96   String() : m_str(NULL) {}
97 
98   String(const char *format, ...) : m_str(NULL) {
99     va_list args;
100     va_start(args, format);
101     vprintf(format, args);
102     va_end(args);
103   }
104 
105   ~String() { reset(); }
106 
107   void reset(char *s = NULL) {
108     if (m_str)
109       ::free(m_str);
110     m_str = s;
111   }
112 
113   const char *c_str() const { return m_str; }
114 
115   void printf(const char *format, ...) {
116     va_list args;
117     va_start(args, format);
118     vprintf(format, args);
119     va_end(args);
120   }
121   void vprintf(const char *format, va_list args) {
122     reset();
123     ::vasprintf(&m_str, format, args);
124   }
125 
126   void log(int log_fd) {
127     if (m_str && log_fd >= 0) {
128       const int len = strlen(m_str);
129       if (len > 0) {
130         write(log_fd, m_str, len);
131         const char last_char = m_str[len - 1];
132         if (!(last_char == '\n' || last_char == '\r'))
133           write(log_fd, "\n", 1);
134       }
135     }
136   }
137 
138 protected:
139   char *m_str;
140 
141 private:
142   DISALLOW_COPY_AND_ASSIGN(String);
143 };
144 
145 //----------------------------------------------------------------------
146 // Type definitions
147 //----------------------------------------------------------------------
148 typedef std::vector<void *> Frames;
149 class FDEvent;
150 typedef std::vector<void *> Frames;
151 typedef std::tr1::shared_ptr<FDEvent> FDEventSP;
152 typedef std::tr1::shared_ptr<String> StringSP;
153 
154 //----------------------------------------------------------------------
155 // FDEvent
156 //
157 // A class that describes a file desciptor event.
158 //
159 // File descriptor events fall into one of two categories: create events
160 // and delete events.
161 //----------------------------------------------------------------------
162 class FDEvent {
163 public:
164   FDEvent(int fd, int err, const StringSP &string_sp, bool is_create,
165           const Frames &frames)
166       : m_string_sp(string_sp), m_frames(frames.begin(), frames.end()),
167         m_fd(fd), m_err(err), m_is_create(is_create) {}
168 
169   ~FDEvent() {}
170 
171   bool IsCreateEvent() const { return m_is_create; }
172 
173   bool IsDeleteEvent() const { return !m_is_create; }
174 
175   Frames &GetFrames() { return m_frames; }
176 
177   const Frames &GetFrames() const { return m_frames; }
178 
179   int GetFD() const { return m_fd; }
180 
181   int GetError() const { return m_err; }
182 
183   void Dump(int log_fd) const;
184 
185   void SetCreateEvent(FDEventSP &create_event_sp) {
186     m_create_event_sp = create_event_sp;
187   }
188 
189 private:
190   // A shared pointer to a String that describes this event in
191   // detail (all args and return and error values)
192   StringSP m_string_sp;
193   // The frames for the stack backtrace for this event
194   Frames m_frames;
195   // If this is a file descriptor delete event, this might contain
196   // the correspoding file descriptor create event
197   FDEventSP m_create_event_sp;
198   // The file descriptor for this event
199   int m_fd;
200   // The error code (if any) for this event
201   int m_err;
202   // True if this event is a file descriptor create event, false
203   // if it is a file descriptor delete event
204   bool m_is_create;
205 };
206 
207 //----------------------------------------------------------------------
208 // Templatized class that will save errno only if the "value" it is
209 // constructed with is equal to INVALID. When the class goes out of
210 // scope, it will restore errno if it was saved.
211 //----------------------------------------------------------------------
212 template <int INVALID> class Errno {
213 public:
214   // Save errno only if we are supposed to
215   Errno(int value)
216       : m_saved_errno((value == INVALID) ? errno : 0),
217         m_restore(value == INVALID) {}
218 
219   // Restore errno only if we are supposed to
220   ~Errno() {
221     if (m_restore)
222       errno = m_saved_errno;
223   }
224 
225   // Accessor for the saved value of errno
226   int get_errno() const { return m_saved_errno; }
227 
228 protected:
229   const int m_saved_errno;
230   const bool m_restore;
231 };
232 
233 typedef Errno<-1> InvalidFDErrno;
234 typedef Errno<-1> NegativeErrorErrno;
235 typedef std::vector<FDEventSP> FDEventArray;
236 typedef std::map<int, FDEventArray> FDEventMap;
237 
238 //----------------------------------------------------------------------
239 // Globals
240 //----------------------------------------------------------------------
241 // Global event map that contains all file descriptor events. As file
242 // descriptor create and close events come in, they will get filled
243 // into this map (protected by g_mutex). When a file descriptor close
244 // event is detected, the open event will be removed and placed into
245 // the close event so if something tries to double close a file
246 // descriptor we can show the previous close event and the file
247 // desctiptor event that created it. When a new file descriptor create
248 // event comes in, we will remove the previous one for that file
249 // desctiptor unless the environment variable
250 // "FileDescriptorStackLoggingNoCompact"
251 // is set. The file desctiptor history can be accessed using the
252 // get_fd_history() function.
253 static FDEventMap g_fd_event_map;
254 // A mutex to protect access to our data structures in g_fd_event_map
255 // and also our logging messages
256 static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
257 // Log all file descriptor create and close events by default. Only log
258 // warnings and erros if the "FileDescriptorMinimalLogging" environment
259 // variable is set.
260 static int g_log_all_calls = 1;
261 // We compact the file descriptor events by default. Set the environment
262 // varible "FileDescriptorStackLoggingNoCompact" to keep a full history.
263 static int g_compact = 1;
264 // The current process ID
265 static int g_pid = -1;
266 static bool g_enabled = true;
267 //----------------------------------------------------------------------
268 // Mutex class that will lock a mutex when it is constructed, and unlock
269 // it when is goes out of scope
270 //----------------------------------------------------------------------
271 class Locker {
272 public:
273   Locker(pthread_mutex_t *mutex_ptr) : m_mutex_ptr(mutex_ptr) {
274     ::pthread_mutex_lock(m_mutex_ptr);
275   }
276 
277   // This allows clients to test try and acquire the mutex...
278   Locker(pthread_mutex_t *mutex_ptr, bool &lock_acquired) : m_mutex_ptr(NULL) {
279     lock_acquired = ::pthread_mutex_trylock(mutex_ptr) == 0;
280     if (lock_acquired)
281       m_mutex_ptr = mutex_ptr;
282   }
283 
284   ~Locker() {
285     if (m_mutex_ptr)
286       ::pthread_mutex_unlock(m_mutex_ptr);
287   }
288 
289 protected:
290   pthread_mutex_t *m_mutex_ptr;
291 };
292 
293 static void log(const char *format, ...) __attribute__((format(printf, 1, 2)));
294 
295 static void log(int log_fd, const FDEvent *event, const char *format, ...)
296     __attribute__((format(printf, 3, 4)));
297 
298 static void backtrace_log(const char *format, ...)
299     __attribute__((format(printf, 1, 2)));
300 
301 static void backtrace_error(const char *format, ...)
302     __attribute__((format(printf, 1, 2)));
303 
304 static void log_to_fd(int log_fd, const char *format, ...)
305     __attribute__((format(printf, 2, 3)));
306 
307 static inline size_t get_backtrace(Frames &frame_buffer,
308                                    size_t frames_to_remove) {
309   void *frames[2048];
310   int count = ::backtrace(&frames[0], sizeof(frames) / sizeof(void *));
311   if (count > frames_to_remove)
312     frame_buffer.assign(&frames[frames_to_remove], &frames[count]);
313   else
314     frame_buffer.assign(&frames[0], &frames[count]);
315   while (frame_buffer.back() < (void *)1024)
316     frame_buffer.pop_back();
317   return frame_buffer.size();
318 }
319 
320 static int g_log_fd = STDOUT_FILENO;
321 static int g_initialized = 0;
322 
323 const char *get_process_fullpath(bool force = false) {
324   static char g_process_fullpath[PATH_MAX] = {0};
325   if (force || g_process_fullpath[0] == '\0') {
326     // If DST is NULL, then return the number of bytes needed.
327     uint32_t len = sizeof(g_process_fullpath);
328     if (_NSGetExecutablePath(g_process_fullpath, &len) != 0)
329       strncpy(g_process_fullpath, "<error>", sizeof(g_process_fullpath));
330   }
331   return g_process_fullpath;
332 }
333 
334 // Returns the current process ID, or -1 if inserposing not enabled for
335 // this process
336 static int get_interposed_pid() {
337   if (!g_enabled)
338     return -1;
339 
340   const pid_t pid = getpid();
341   if (g_pid != pid) {
342     if (g_pid == -1) {
343       g_pid = pid;
344       log("Interposing file descriptor create and delete functions for %s "
345           "(pid=%i)\n",
346           get_process_fullpath(true), pid);
347     } else {
348       log("pid=%i: disabling interposing file descriptor create and delete "
349           "functions for child process %s (pid=%i)\n",
350           g_pid, get_process_fullpath(true), pid);
351       g_enabled = false;
352       return -1;
353     }
354     // Log when our process changes
355   }
356   return g_pid;
357 }
358 
359 static int get_logging_fd() {
360   if (!g_enabled)
361     return -1;
362 
363   if (!g_initialized) {
364     g_initialized = 1;
365 
366     const pid_t pid = get_interposed_pid();
367 
368     if (g_enabled) {
369       // Keep all stack info around for all fd create and delete calls.
370       // Otherwise we will remove the fd create call when a corresponding
371       // fd delete call is received
372       if (getenv("FileDescriptorStackLoggingNoCompact"))
373         g_compact = 0;
374 
375       if (getenv("FileDescriptorMinimalLogging"))
376         g_log_all_calls = 0;
377 
378       const char *log_path = getenv("FileDescriptorLogFile");
379       if (log_path)
380         g_log_fd = ::creat(log_path, 0660);
381       else
382         g_log_fd = STDOUT_FILENO;
383 
384       // Only let this interposing happen on the first time this matches
385       // and stop this from happening so any child processes don't also
386       // log their file descriptors
387       ::unsetenv("DYLD_INSERT_LIBRARIES");
388     } else {
389       log("pid=%i: logging disabled\n", getpid());
390     }
391   }
392   return g_log_fd;
393 }
394 
395 void log_to_fd(int log_fd, const char *format, va_list args) {
396   if (format && format[0] && log_fd >= 0) {
397     char buffer[PATH_MAX];
398     const int count = ::vsnprintf(buffer, sizeof(buffer), format, args);
399     if (count > 0)
400       write(log_fd, buffer, count);
401   }
402 }
403 
404 void log_to_fd(int log_fd, const char *format, ...) {
405   if (format && format[0]) {
406     va_list args;
407     va_start(args, format);
408     log_to_fd(log_fd, format, args);
409     va_end(args);
410   }
411 }
412 
413 void log(const char *format, va_list args) {
414   log_to_fd(get_logging_fd(), format, args);
415 }
416 
417 void log(const char *format, ...) {
418   if (format && format[0]) {
419     va_list args;
420     va_start(args, format);
421     log(format, args);
422     va_end(args);
423   }
424 }
425 
426 void log(int log_fd, const FDEvent *event, const char *format, ...) {
427   if (format && format[0]) {
428     va_list args;
429     va_start(args, format);
430     log_to_fd(log_fd, format, args);
431     va_end(args);
432   }
433   if (event)
434     event->Dump(log_fd);
435 }
436 
437 void FDEvent::Dump(int log_fd) const {
438   if (log_fd >= 0) {
439     log_to_fd(log_fd, "%s\n", m_string_sp->c_str());
440     if (!m_frames.empty())
441       ::backtrace_symbols_fd(m_frames.data(), m_frames.size(), log_fd);
442 
443     if (m_create_event_sp) {
444       log_to_fd(log_fd, "\nfd=%i was created with this event:\n", m_fd);
445       m_create_event_sp->Dump(log_fd);
446       log_to_fd(log_fd, "\n");
447     }
448   }
449 }
450 
451 void backtrace_log(const char *format, ...) {
452   const int log_fd = get_logging_fd();
453   if (log_fd >= 0) {
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 void backtrace_error(const char *format, ...) {
468   const int pid = get_interposed_pid();
469   if (pid >= 0) {
470     const int log_fd = get_logging_fd();
471     if (log_fd >= 0) {
472       log("\nerror: %s (pid=%i): ", get_process_fullpath(), pid);
473 
474       if (format && format[0]) {
475         va_list args;
476         va_start(args, format);
477         log(format, args);
478         va_end(args);
479       }
480 
481       Frames frames;
482       if (get_backtrace(frames, 2))
483         ::backtrace_symbols_fd(frames.data(), frames.size(), log_fd);
484     }
485   }
486 }
487 
488 void save_backtrace(int fd, int err, const StringSP &string_sp,
489                     bool is_create) {
490   Frames frames;
491   get_backtrace(frames, 2);
492 
493   FDEventSP fd_event_sp(new FDEvent(fd, err, string_sp, is_create, frames));
494 
495   FDEventMap::iterator pos = g_fd_event_map.find(fd);
496 
497   if (pos != g_fd_event_map.end()) {
498     // We have history for this fd...
499 
500     FDEventArray &event_array = g_fd_event_map[fd];
501     if (fd_event_sp->IsCreateEvent()) {
502       // The current fd event is a function that creates
503       // a descriptor, check in case last event was
504       // a create event.
505       if (event_array.back()->IsCreateEvent()) {
506         const int log_fd = get_logging_fd();
507         // Two fd create functions in a row, we missed
508         // a function that closes a fd...
509         log(log_fd, fd_event_sp.get(), "\nwarning: unmatched file descriptor "
510                                        "create event fd=%i (we missed a file "
511                                        "descriptor close event):\n",
512             fd);
513       } else if (g_compact) {
514         // We are compacting so we remove previous create event
515         // when we get the correspinding delete event
516         event_array.pop_back();
517       }
518     } else {
519       // The current fd event is a function that deletes
520       // a descriptor, check in case last event for this
521       // fd was a delete event (double close!)
522       if (event_array.back()->IsDeleteEvent()) {
523         const int log_fd = get_logging_fd();
524         // Two fd delete functions in a row, we must
525         // have missed some function that opened a descriptor
526         log(log_fd, fd_event_sp.get(), "\nwarning: unmatched file descriptor "
527                                        "close event for fd=%d (we missed the "
528                                        "file descriptor create event):\n",
529             fd);
530       } else if (g_compact) {
531         // Since this is a close event, we want to remember the open event
532         // that this close if for...
533         fd_event_sp->SetCreateEvent(event_array.back());
534         // We are compacting so we remove previous create event
535         // when we get the correspinding delete event
536         event_array.pop_back();
537       }
538     }
539 
540     event_array.push_back(fd_event_sp);
541   } else {
542     g_fd_event_map[fd].push_back(fd_event_sp);
543   }
544 }
545 
546 //----------------------------------------------------------------------
547 // socket() interpose function
548 //----------------------------------------------------------------------
549 extern "C" int socket$__interposed__(int domain, int type, int protocol) {
550   const int pid = get_interposed_pid();
551   if (pid >= 0) {
552     Locker locker(&g_mutex);
553     const int fd = ::socket(domain, type, protocol);
554     InvalidFDErrno fd_errno(fd);
555     StringSP description_sp(new String);
556     if (fd == -1)
557       description_sp->printf("pid=%i: socket (domain = %i, type = %i, protocol "
558                              "= %i) => fd=%i  errno = %i",
559                              pid, domain, type, protocol, fd,
560                              fd_errno.get_errno());
561     else
562       description_sp->printf(
563           "pid=%i: socket (domain = %i, type = %i, protocol = %i) => fd=%i",
564           pid, domain, type, protocol, fd);
565     if (g_log_all_calls)
566       description_sp->log(get_logging_fd());
567     if (fd >= 0)
568       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
569     return fd;
570   } else {
571     return ::socket(domain, type, protocol);
572   }
573 }
574 
575 //----------------------------------------------------------------------
576 // socketpair() interpose function
577 //----------------------------------------------------------------------
578 extern "C" int socketpair$__interposed__(int domain, int type, int protocol,
579                                          int fds[2]) {
580   const int pid = get_interposed_pid();
581   if (pid >= 0) {
582     Locker locker(&g_mutex);
583     fds[0] = -1;
584     fds[1] = -1;
585     const int err = socketpair(domain, type, protocol, fds);
586     NegativeErrorErrno err_errno(err);
587     StringSP description_sp(
588         new String("pid=%i: socketpair (domain=%i, type=%i, protocol=%i, "
589                    "{fd=%i, fd=%i}) -> err=%i",
590                    pid, domain, type, protocol, fds[0], fds[1], err));
591     if (g_log_all_calls)
592       description_sp->log(get_logging_fd());
593     if (fds[0] >= 0)
594       save_backtrace(fds[0], err_errno.get_errno(), description_sp, true);
595     if (fds[1] >= 0)
596       save_backtrace(fds[1], err_errno.get_errno(), description_sp, true);
597     return err;
598   } else {
599     return socketpair(domain, type, protocol, fds);
600   }
601 }
602 
603 //----------------------------------------------------------------------
604 // open() interpose function
605 //----------------------------------------------------------------------
606 extern "C" int open$__interposed__(const char *path, int oflag, int mode) {
607   const int pid = get_interposed_pid();
608   if (pid >= 0) {
609     Locker locker(&g_mutex);
610     int fd = -2;
611     StringSP description_sp(new String);
612     if (oflag & O_CREAT) {
613       fd = ::open(path, oflag, mode);
614       description_sp->printf(
615           "pid=%i: open (path = '%s', oflag = %i, mode = %i) -> fd=%i", pid,
616           path, oflag, mode, fd);
617     } else {
618       fd = ::open(path, oflag);
619       description_sp->printf("pid=%i: open (path = '%s', oflag = %i) -> fd=%i",
620                              pid, path, oflag, fd);
621     }
622 
623     InvalidFDErrno fd_errno(fd);
624     if (g_log_all_calls)
625       description_sp->log(get_logging_fd());
626     if (fd >= 0)
627       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
628     return fd;
629   } else {
630     return ::open(path, oflag, mode);
631   }
632 }
633 
634 //----------------------------------------------------------------------
635 // open$NOCANCEL() interpose function
636 //----------------------------------------------------------------------
637 extern "C" int open$NOCANCEL$__interposed__(const char *path, int oflag,
638                                             int mode) {
639   const int pid = get_interposed_pid();
640   if (pid >= 0) {
641     Locker locker(&g_mutex);
642     const int fd = ::open$NOCANCEL(path, oflag, mode);
643     InvalidFDErrno fd_errno(fd);
644     StringSP description_sp(new String(
645         "pid=%i: open$NOCANCEL (path = '%s', oflag = %i, mode = %i) -> fd=%i",
646         pid, path, oflag, mode, fd));
647     if (g_log_all_calls)
648       description_sp->log(get_logging_fd());
649     if (fd >= 0)
650       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
651     return fd;
652   } else {
653     return ::open$NOCANCEL(path, oflag, mode);
654   }
655 }
656 
657 //----------------------------------------------------------------------
658 // __open_extended() interpose function
659 //----------------------------------------------------------------------
660 extern "C" int __open_extended$__interposed__(const char *path, int oflag,
661                                               uid_t uid, gid_t gid, int mode,
662                                               struct kauth_filesec *fsacl) {
663   const int pid = get_interposed_pid();
664   if (pid >= 0) {
665     Locker locker(&g_mutex);
666     const int fd = ::__open_extended(path, oflag, uid, gid, mode, fsacl);
667     InvalidFDErrno fd_errno(fd);
668     StringSP description_sp(
669         new String("pid=%i: __open_extended (path='%s', oflag=%i, uid=%i, "
670                    "gid=%i, mode=%i, fsacl=%p) -> fd=%i",
671                    pid, path, oflag, uid, gid, mode, fsacl, fd));
672     if (g_log_all_calls)
673       description_sp->log(get_logging_fd());
674     if (fd >= 0)
675       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
676     return fd;
677   } else {
678     return ::__open_extended(path, oflag, uid, gid, mode, fsacl);
679   }
680 }
681 
682 //----------------------------------------------------------------------
683 // kqueue() interpose function
684 //----------------------------------------------------------------------
685 extern "C" int kqueue$__interposed__(void) {
686   const int pid = get_interposed_pid();
687   if (pid >= 0) {
688     Locker locker(&g_mutex);
689     const int fd = ::kqueue();
690     InvalidFDErrno fd_errno(fd);
691     StringSP description_sp(new String("pid=%i: kqueue () -> fd=%i", pid, fd));
692     if (g_log_all_calls)
693       description_sp->log(get_logging_fd());
694     if (fd >= 0)
695       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
696     return fd;
697   } else {
698     return ::kqueue();
699   }
700 }
701 
702 //----------------------------------------------------------------------
703 // shm_open() interpose function
704 //----------------------------------------------------------------------
705 extern "C" int shm_open$__interposed__(const char *path, int oflag, int mode) {
706   const int pid = get_interposed_pid();
707   if (pid >= 0) {
708     Locker locker(&g_mutex);
709     const int fd = ::shm_open(path, oflag, mode);
710     InvalidFDErrno fd_errno(fd);
711     StringSP description_sp(new String(
712         "pid=%i: shm_open (path = '%s', oflag = %i, mode = %i) -> fd=%i", pid,
713         path, oflag, mode, fd));
714     if (g_log_all_calls)
715       description_sp->log(get_logging_fd());
716     if (fd >= 0)
717       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
718     return fd;
719   } else {
720     return ::shm_open(path, oflag, mode);
721   }
722 }
723 
724 //----------------------------------------------------------------------
725 // accept() interpose function
726 //----------------------------------------------------------------------
727 extern "C" int accept$__interposed__(int socket, struct sockaddr *address,
728                                      socklen_t *address_len) {
729   const int pid = get_interposed_pid();
730   if (pid >= 0) {
731     Locker locker(&g_mutex);
732     const int fd = ::accept(socket, address, address_len);
733     InvalidFDErrno fd_errno(fd);
734     StringSP description_sp(new String(
735         "pid=%i: accept (socket=%i, ...) -> fd=%i", pid, socket, fd));
736     if (g_log_all_calls)
737       description_sp->log(get_logging_fd());
738     if (fd >= 0)
739       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
740     return fd;
741   } else {
742     return ::accept(socket, address, address_len);
743   }
744 }
745 
746 //----------------------------------------------------------------------
747 // accept$NOCANCEL() interpose function
748 //----------------------------------------------------------------------
749 extern "C" int accept$NOCANCEL$__interposed__(int socket,
750                                               struct sockaddr *address,
751                                               socklen_t *address_len) {
752   const int pid = get_interposed_pid();
753   if (pid >= 0) {
754     Locker locker(&g_mutex);
755     const int fd = ::accept$NOCANCEL(socket, address, address_len);
756     InvalidFDErrno fd_errno(fd);
757     StringSP description_sp(new String(
758         "pid=%i: accept$NOCANCEL (socket=%i, ...) -> fd=%i", pid, socket, fd));
759     if (g_log_all_calls)
760       description_sp->log(get_logging_fd());
761     if (fd >= 0)
762       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
763     return fd;
764   } else {
765     return ::accept$NOCANCEL(socket, address, address_len);
766   }
767 }
768 
769 //----------------------------------------------------------------------
770 // dup() interpose function
771 //----------------------------------------------------------------------
772 extern "C" int dup$__interposed__(int fd2) {
773   const int pid = get_interposed_pid();
774   if (pid >= 0) {
775     Locker locker(&g_mutex);
776     const int fd = ::dup(fd2);
777     InvalidFDErrno fd_errno(fd);
778     StringSP description_sp(
779         new String("pid=%i: dup (fd2=%i) -> fd=%i", pid, fd2, fd));
780     if (g_log_all_calls)
781       description_sp->log(get_logging_fd());
782     if (fd >= 0)
783       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
784     return fd;
785   } else {
786     return ::dup(fd2);
787   }
788 }
789 
790 //----------------------------------------------------------------------
791 // dup2() interpose function
792 //----------------------------------------------------------------------
793 extern "C" int dup2$__interposed__(int fd1, int fd2) {
794   const int pid = get_interposed_pid();
795   if (pid >= 0) {
796     Locker locker(&g_mutex);
797     // If "fd2" is already opened, it will be closed during the
798     // dup2 call below, so we need to see if we have fd2 in our
799     // open map and treat it as a close(fd2)
800     FDEventMap::iterator pos = g_fd_event_map.find(fd2);
801     StringSP dup2_close_description_sp(
802         new String("pid=%i: dup2 (fd1=%i, fd2=%i) -> will close (fd=%i)", pid,
803                    fd1, fd2, fd2));
804     if (pos != g_fd_event_map.end() && pos->second.back()->IsCreateEvent())
805       save_backtrace(fd2, 0, dup2_close_description_sp, false);
806 
807     const int fd = ::dup2(fd1, fd2);
808     InvalidFDErrno fd_errno(fd);
809     StringSP description_sp(new String("pid=%i: dup2 (fd1=%i, fd2=%i) -> fd=%i",
810                                        pid, fd1, fd2, fd));
811     if (g_log_all_calls)
812       description_sp->log(get_logging_fd());
813 
814     if (fd >= 0)
815       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
816     return fd;
817   } else {
818     return ::dup2(fd1, fd2);
819   }
820 }
821 
822 //----------------------------------------------------------------------
823 // close() interpose function
824 //----------------------------------------------------------------------
825 extern "C" int close$__interposed__(int fd) {
826   const int pid = get_interposed_pid();
827   if (pid >= 0) {
828     Locker locker(&g_mutex);
829     const int err = close(fd);
830     NegativeErrorErrno err_errno(err);
831     StringSP description_sp(new String);
832     if (err == -1)
833       description_sp->printf("pid=%i: close (fd=%i) => %i errno = %i (%s))",
834                              pid, fd, err, err_errno.get_errno(),
835                              strerror(err_errno.get_errno()));
836     else
837       description_sp->printf("pid=%i: close (fd=%i) => %i", pid, fd, err);
838     if (g_log_all_calls)
839       description_sp->log(get_logging_fd());
840 
841     if (err == 0) {
842       if (fd >= 0)
843         save_backtrace(fd, err, description_sp, false);
844     } else if (err == -1) {
845       if (err_errno.get_errno() == EBADF && fd != -1) {
846         backtrace_error("close (fd=%d) resulted in EBADF:\n", fd);
847 
848         FDEventMap::iterator pos = g_fd_event_map.find(fd);
849         if (pos != g_fd_event_map.end()) {
850           log(get_logging_fd(), pos->second.back().get(),
851               "\nfd=%d was previously %s with this event:\n", fd,
852               pos->second.back()->IsCreateEvent() ? "opened" : "closed");
853         }
854       }
855     }
856     return err;
857   } else {
858     return close(fd);
859   }
860 }
861 
862 //----------------------------------------------------------------------
863 // close$NOCANCEL() interpose function
864 //----------------------------------------------------------------------
865 extern "C" int close$NOCANCEL$__interposed__(int fd) {
866   const int pid = get_interposed_pid();
867   if (pid >= 0) {
868     Locker locker(&g_mutex);
869     const int err = close$NOCANCEL(fd);
870     NegativeErrorErrno err_errno(err);
871     StringSP description_sp(new String);
872     if (err == -1)
873       description_sp->printf(
874           "pid=%i: close$NOCANCEL (fd=%i) => %i errno = %i (%s))", pid, fd, err,
875           err_errno.get_errno(), strerror(err_errno.get_errno()));
876     else
877       description_sp->printf("pid=%i: close$NOCANCEL (fd=%i) => %i", pid, fd,
878                              err);
879     if (g_log_all_calls)
880       description_sp->log(get_logging_fd());
881 
882     if (err == 0) {
883       if (fd >= 0)
884         save_backtrace(fd, err, description_sp, false);
885     } else if (err == -1) {
886       if (err_errno.get_errno() == EBADF && fd != -1) {
887         backtrace_error("close$NOCANCEL (fd=%d) resulted in EBADF\n:", fd);
888 
889         FDEventMap::iterator pos = g_fd_event_map.find(fd);
890         if (pos != g_fd_event_map.end()) {
891           log(get_logging_fd(), pos->second.back().get(),
892               "\nfd=%d was previously %s with this event:\n", fd,
893               pos->second.back()->IsCreateEvent() ? "opened" : "closed");
894         }
895       }
896     }
897     return err;
898   } else {
899     return close$NOCANCEL(fd);
900   }
901 }
902 
903 //----------------------------------------------------------------------
904 // pipe() interpose function
905 //----------------------------------------------------------------------
906 extern "C" int pipe$__interposed__(int fds[2]) {
907   const int pid = get_interposed_pid();
908   if (pid >= 0) {
909     Locker locker(&g_mutex);
910     fds[0] = -1;
911     fds[1] = -1;
912     const int err = pipe(fds);
913     const int saved_errno = errno;
914     StringSP description_sp(new String(
915         "pid=%i: pipe ({fd=%i, fd=%i}) -> err=%i", pid, fds[0], fds[1], err));
916     if (g_log_all_calls)
917       description_sp->log(get_logging_fd());
918     if (fds[0] >= 0)
919       save_backtrace(fds[0], saved_errno, description_sp, true);
920     if (fds[1] >= 0)
921       save_backtrace(fds[1], saved_errno, description_sp, true);
922     errno = saved_errno;
923     return err;
924   } else {
925     return pipe(fds);
926   }
927 }
928 
929 //----------------------------------------------------------------------
930 // get_fd_history()
931 //
932 // This function allows runtime access to the file descriptor history.
933 //
934 // @param[in] log_fd
935 //      The file descriptor to log to
936 //
937 // @param[in] fd
938 //      The file descriptor whose history should be dumped
939 //----------------------------------------------------------------------
940 extern "C" void get_fd_history(int log_fd, int fd) {
941   // "create" below needs to be outside of the mutex locker scope
942   if (log_fd >= 0) {
943     bool got_lock = false;
944     Locker locker(&g_mutex, got_lock);
945     if (got_lock) {
946       FDEventMap::iterator pos = g_fd_event_map.find(fd);
947       log_to_fd(log_fd, "Dumping file descriptor history for fd=%i:\n", fd);
948       if (pos != g_fd_event_map.end()) {
949         FDEventArray &event_array = g_fd_event_map[fd];
950         const size_t num_events = event_array.size();
951         for (size_t i = 0; i < num_events; ++i)
952           event_array[i]->Dump(log_fd);
953       } else {
954         log_to_fd(log_fd, "error: no file descriptor events found for fd=%i\n",
955                   fd);
956       }
957     } else {
958       log_to_fd(log_fd, "error: fd event mutex is locked...\n");
959     }
960   }
961 }
962 
963 //----------------------------------------------------------------------
964 // Interposing
965 //----------------------------------------------------------------------
966 // FD creation routines
967 DYLD_INTERPOSE(accept$__interposed__, accept);
968 DYLD_INTERPOSE(accept$NOCANCEL$__interposed__, accept$NOCANCEL);
969 DYLD_INTERPOSE(dup$__interposed__, dup);
970 DYLD_INTERPOSE(dup2$__interposed__, dup2);
971 DYLD_INTERPOSE(kqueue$__interposed__, kqueue);
972 DYLD_INTERPOSE(open$__interposed__, open);
973 DYLD_INTERPOSE(open$NOCANCEL$__interposed__, open$NOCANCEL);
974 DYLD_INTERPOSE(__open_extended$__interposed__, __open_extended);
975 DYLD_INTERPOSE(pipe$__interposed__, pipe);
976 DYLD_INTERPOSE(shm_open$__interposed__, shm_open);
977 DYLD_INTERPOSE(socket$__interposed__, socket);
978 DYLD_INTERPOSE(socketpair$__interposed__, socketpair);
979 
980 // FD deleting routines
981 DYLD_INTERPOSE(close$__interposed__, close);
982 DYLD_INTERPOSE(close$NOCANCEL$__interposed__, close$NOCANCEL);
983 
984 } // namespace fd_interposing
985