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 extern "C" {
72 int accept$NOCANCEL(int, struct sockaddr *__restrict, socklen_t *__restrict);
73 int close$NOCANCEL(int);
74 int open$NOCANCEL(const char *, int, ...);
75 int __open_extended(const char *, int, uid_t, gid_t, int,
76                     struct kauth_filesec *);
77 }
78 
79 namespace fd_interposing {
80 
81 // String class so we can get formatted strings without having to worry
82 // about the memory storage since it will allocate the memory it needs.
83 class String {
84 public:
String()85   String() : m_str(NULL) {}
86 
String(const char * format,...)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 
~String()94   ~String() { reset(); }
95 
reset(char * s=NULL)96   void reset(char *s = NULL) {
97     if (m_str)
98       ::free(m_str);
99     m_str = s;
100   }
101 
c_str() const102   const char *c_str() const { return m_str; }
103 
printf(const char * format,...)104   void printf(const char *format, ...) {
105     va_list args;
106     va_start(args, format);
107     vprintf(format, args);
108     va_end(args);
109   }
vprintf(const char * format,va_list args)110   void vprintf(const char *format, va_list args) {
111     reset();
112     ::vasprintf(&m_str, format, args);
113   }
114 
log(int log_fd)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 
127 protected:
128   char *m_str;
129 
130 private:
131   String(const String &) = delete;
132   const String &operator=(const String &) = delete;
133 };
134 
135 // Type definitions
136 typedef std::vector<void *> Frames;
137 class FDEvent;
138 typedef std::vector<void *> Frames;
139 typedef std::tr1::shared_ptr<FDEvent> FDEventSP;
140 typedef std::tr1::shared_ptr<String> StringSP;
141 
142 // FDEvent
143 //
144 // A class that describes a file descriptor event.
145 //
146 // File descriptor events fall into one of two categories: create events
147 // and delete events.
148 class FDEvent {
149 public:
FDEvent(int fd,int err,const StringSP & string_sp,bool is_create,const Frames & frames)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 
~FDEvent()155   ~FDEvent() {}
156 
IsCreateEvent() const157   bool IsCreateEvent() const { return m_is_create; }
158 
IsDeleteEvent() const159   bool IsDeleteEvent() const { return !m_is_create; }
160 
GetFrames()161   Frames &GetFrames() { return m_frames; }
162 
GetFrames() const163   const Frames &GetFrames() const { return m_frames; }
164 
GetFD() const165   int GetFD() const { return m_fd; }
166 
GetError() const167   int GetError() const { return m_err; }
168 
169   void Dump(int log_fd) const;
170 
SetCreateEvent(FDEventSP & create_event_sp)171   void SetCreateEvent(FDEventSP &create_event_sp) {
172     m_create_event_sp = create_event_sp;
173   }
174 
175 private:
176   // A shared pointer to a String that describes this event in
177   // detail (all args and return and error values)
178   StringSP m_string_sp;
179   // The frames for the stack backtrace for this event
180   Frames m_frames;
181   // If this is a file descriptor delete event, this might contain
182   // the corresponding file descriptor create event
183   FDEventSP m_create_event_sp;
184   // The file descriptor for this event
185   int m_fd;
186   // The error code (if any) for this event
187   int m_err;
188   // True if this event is a file descriptor create event, false
189   // if it is a file descriptor delete event
190   bool m_is_create;
191 };
192 
193 // Templatized class that will save errno only if the "value" it is
194 // constructed with is equal to INVALID. When the class goes out of
195 // scope, it will restore errno if it was saved.
196 template <int INVALID> class Errno {
197 public:
198   // Save errno only if we are supposed to
Errno(int value)199   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 to
~Errno()204   ~Errno() {
205     if (m_restore)
206       errno = m_saved_errno;
207   }
208 
209   // Accessor for the saved value of errno
get_errno() const210   int get_errno() const { return m_saved_errno; }
211 
212 protected:
213   const int m_saved_errno;
214   const bool m_restore;
215 };
216 
217 typedef Errno<-1> InvalidFDErrno;
218 typedef Errno<-1> NegativeErrorErrno;
219 typedef std::vector<FDEventSP> FDEventArray;
220 typedef std::map<int, FDEventArray> FDEventMap;
221 
222 // Globals
223 // Global event map that contains all file descriptor events. As file
224 // descriptor create and close events come in, they will get filled
225 // into this map (protected by g_mutex). When a file descriptor close
226 // event is detected, the open event will be removed and placed into
227 // the close event so if something tries to double close a file
228 // descriptor we can show the previous close event and the file
229 // descriptor event that created it. When a new file descriptor create
230 // event comes in, we will remove the previous one for that file
231 // descriptor unless the environment variable
232 // "FileDescriptorStackLoggingNoCompact"
233 // is set. The file descriptor history can be accessed using the
234 // get_fd_history() function.
235 static FDEventMap g_fd_event_map;
236 // A mutex to protect access to our data structures in g_fd_event_map
237 // and also our logging messages
238 static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
239 // Log all file descriptor create and close events by default. Only log
240 // warnings and errors if the "FileDescriptorMinimalLogging" environment
241 // variable is set.
242 static int g_log_all_calls = 1;
243 // We compact the file descriptor events by default. Set the environment
244 // varible "FileDescriptorStackLoggingNoCompact" to keep a full history.
245 static int g_compact = 1;
246 // The current process ID
247 static int g_pid = -1;
248 static bool g_enabled = true;
249 // Mutex class that will lock a mutex when it is constructed, and unlock
250 // it when is goes out of scope
251 class Locker {
252 public:
Locker(pthread_mutex_t * mutex_ptr)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...
Locker(pthread_mutex_t * mutex_ptr,bool & lock_acquired)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 
~Locker()264   ~Locker() {
265     if (m_mutex_ptr)
266       ::pthread_mutex_unlock(m_mutex_ptr);
267   }
268 
269 protected:
270   pthread_mutex_t *m_mutex_ptr;
271 };
272 
273 static void log(const char *format, ...) __attribute__((format(printf, 1, 2)));
274 
275 static void log(int log_fd, const FDEvent *event, const char *format, ...)
276     __attribute__((format(printf, 3, 4)));
277 
278 static void backtrace_log(const char *format, ...)
279     __attribute__((format(printf, 1, 2)));
280 
281 static void backtrace_error(const char *format, ...)
282     __attribute__((format(printf, 1, 2)));
283 
284 static void log_to_fd(int log_fd, const char *format, ...)
285     __attribute__((format(printf, 2, 3)));
286 
get_backtrace(Frames & frame_buffer,size_t frames_to_remove)287 static 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   else
294     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 
300 static int g_log_fd = STDOUT_FILENO;
301 static int g_initialized = 0;
302 
get_process_fullpath(bool force=false)303 const 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 for
315 // this process
get_interposed_pid()316 static 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 changes
335   }
336   return g_pid;
337 }
338 
get_logging_fd()339 static 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 corresponding
351       // fd delete call is received
352       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       else
362         g_log_fd = STDOUT_FILENO;
363 
364       // Only let this interposing happen on the first time this matches
365       // and stop this from happening so any child processes don't also
366       // log their file descriptors
367       ::unsetenv("DYLD_INSERT_LIBRARIES");
368     } else {
369       log("pid=%i: logging disabled\n", getpid());
370     }
371   }
372   return g_log_fd;
373 }
374 
log_to_fd(int log_fd,const char * format,va_list args)375 void 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 
log_to_fd(int log_fd,const char * format,...)384 void 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 
log(const char * format,va_list args)393 void log(const char *format, va_list args) {
394   log_to_fd(get_logging_fd(), format, args);
395 }
396 
log(const char * format,...)397 void 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 
log(int log_fd,const FDEvent * event,const char * format,...)406 void 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 
Dump(int log_fd) const417 void 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 
backtrace_log(const char * format,...)431 void 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 
backtrace_error(const char * format,...)447 void 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 
save_backtrace(int fd,int err,const StringSP & string_sp,bool is_create)468 void 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 creates
483       // a descriptor, check in case last event was
484       // 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 missed
488         // 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 event
495         // when we get the corresponding delete event
496         event_array.pop_back();
497       }
498     } else {
499       // The current fd event is a function that deletes
500       // a descriptor, check in case last event for this
501       // 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 must
505         // have missed some function that opened a descriptor
506         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 event
512         // that this close if for...
513         fd_event_sp->SetCreateEvent(event_array.back());
514         // We are compacting so we remove previous create event
515         // when we get the corresponding delete event
516         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 function
socket$__interposed__(int domain,int type,int protocol)527 extern "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     else
540       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 function
socketpair$__interposed__(int domain,int type,int protocol,int fds[2])554 extern "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 function
open$__interposed__(const char * path,int oflag,int mode)580 extern "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 function
open$NOCANCEL$__interposed__(const char * path,int oflag,int mode)609 extern "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 function
__open_extended$__interposed__(const char * path,int oflag,uid_t uid,gid_t gid,int mode,struct kauth_filesec * fsacl)630 extern "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 function
kqueue$__interposed__(void)653 extern "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 function
shm_open$__interposed__(const char * path,int oflag,int mode)671 extern "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 function
accept$__interposed__(int socket,struct sockaddr * address,socklen_t * address_len)691 extern "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 function
accept$NOCANCEL$__interposed__(int socket,struct sockaddr * address,socklen_t * address_len)711 extern "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 function
dup$__interposed__(int fd2)732 extern "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 function
dup2$__interposed__(int fd1,int fd2)751 extern "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 the
756     // dup2 call below, so we need to see if we have fd2 in our
757     // 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 function
close$__interposed__(int fd)781 extern "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     else
793       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 function
close$NOCANCEL$__interposed__(int fd)819 extern "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     else
831       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 function
pipe$__interposed__(int fds[2])858 extern "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_fd
886 //      The file descriptor to log to
887 //
888 // @param[in] fd
889 //      The file descriptor whose history should be dumped
get_fd_history(int log_fd,int fd)890 extern "C" void get_fd_history(int log_fd, int fd) {
891   // "create" below needs to be outside of the mutex locker scope
892   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 // Interposing
914 // FD creation routines
915 DYLD_INTERPOSE(accept$__interposed__, accept);
916 DYLD_INTERPOSE(accept$NOCANCEL$__interposed__, accept$NOCANCEL);
917 DYLD_INTERPOSE(dup$__interposed__, dup);
918 DYLD_INTERPOSE(dup2$__interposed__, dup2);
919 DYLD_INTERPOSE(kqueue$__interposed__, kqueue);
920 DYLD_INTERPOSE(open$__interposed__, open);
921 DYLD_INTERPOSE(open$NOCANCEL$__interposed__, open$NOCANCEL);
922 DYLD_INTERPOSE(__open_extended$__interposed__, __open_extended);
923 DYLD_INTERPOSE(pipe$__interposed__, pipe);
924 DYLD_INTERPOSE(shm_open$__interposed__, shm_open);
925 DYLD_INTERPOSE(socket$__interposed__, socket);
926 DYLD_INTERPOSE(socketpair$__interposed__, socketpair);
927 
928 // FD deleting routines
929 DYLD_INTERPOSE(close$__interposed__, close);
930 DYLD_INTERPOSE(close$NOCANCEL$__interposed__, close$NOCANCEL);
931 
932 } // namespace fd_interposing
933