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
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 worry
82// 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(ptr: 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(ptr: &m_str, f: format, arg: args);
113 }
114
115 void log(int log_fd) {
116 if (m_str && log_fd >= 0) {
117 const int len = strlen(s: m_str);
118 if (len > 0) {
119 write(fd: log_fd, buf: m_str, n: len);
120 const char last_char = m_str[len - 1];
121 if (!(last_char == '\n' || last_char == '\r'))
122 write(fd: log_fd, buf: "\n", 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 definitions
136typedef 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// 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.
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 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.
196template <int INVALID> class Errno {
197public:
198 // Save errno only if we are supposed to
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
204 ~Errno() {
205 if (m_restore)
206 errno = m_saved_errno;
207 }
208
209 // Accessor for the saved value of errno
210 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// 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.
235static 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
238static 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.
242static 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.
245static int g_compact = 1;
246// The current process ID
247static int g_pid = -1;
248static 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
251class Locker {
252public:
253 Locker(pthread_mutex_t *mutex_ptr) : m_mutex_ptr(mutex_ptr) {
254 ::pthread_mutex_lock(mutex: 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: 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(mutex: 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(array: &frames[0], size: sizeof(frames) / sizeof(void *));
291 if (count > frames_to_remove)
292 frame_buffer.assign(first: &frames[frames_to_remove], last: &frames[count]);
293 else
294 frame_buffer.assign(first: &frames[0], last: &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(dest: g_process_fullpath, src: "<error>", n: 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
316static 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(format: "Interposing file descriptor create and delete functions for %s "
325 "(pid=%i)\n",
326 get_process_fullpath(force: true), pid);
327 } else {
328 log(format: "pid=%i: disabling interposing file descriptor create and delete "
329 "functions for child process %s (pid=%i)\n",
330 g_pid, get_process_fullpath(force: true), pid);
331 g_enabled = false;
332 return -1;
333 }
334 // Log when our process changes
335 }
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 corresponding
351 // fd delete call is received
352 if (getenv(name: "FileDescriptorStackLoggingNoCompact"))
353 g_compact = 0;
354
355 if (getenv(name: "FileDescriptorMinimalLogging"))
356 g_log_all_calls = 0;
357
358 const char *log_path = getenv(name: "FileDescriptorLogFile");
359 if (log_path)
360 g_log_fd = ::creat(file: log_path, mode: 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(name: "DYLD_INSERT_LIBRARIES");
368 } else {
369 log(format: "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(s: buffer, maxlen: sizeof(buffer), format: format, arg: args);
379 if (count > 0)
380 write(fd: log_fd, buf: buffer, n: 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(log_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, format: "%s\n", m_string_sp->c_str());
420 if (!m_frames.empty())
421 ::backtrace_symbols_fd(array: m_frames.data(), size: m_frames.size(), fd: log_fd);
422
423 if (m_create_event_sp) {
424 log_to_fd(log_fd, format: "\nfd=%i was created with this event:\n", m_fd);
425 m_create_event_sp->Dump(log_fd);
426 log_to_fd(log_fd, format: "\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(frame_buffer&: frames, frames_to_remove: 2))
443 ::backtrace_symbols_fd(array: frames.data(), size: frames.size(), fd: 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(format: "\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(frame_buffer&: frames, frames_to_remove: 2))
463 ::backtrace_symbols_fd(array: frames.data(), size: frames.size(), fd: 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(frame_buffer&: frames, frames_to_remove: 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(x: 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, event: fd_event_sp.get(), format: "\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, event: fd_event_sp.get(), format: "\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(x: fd_event_sp);
521 } else {
522 g_fd_event_map[fd].push_back(x: fd_event_sp);
523 }
524}
525
526// socket() interpose function
527extern "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: domain, type: type, protocol: protocol);
532 InvalidFDErrno fd_errno(fd);
533 StringSP description_sp(new String);
534 if (fd == -1)
535 description_sp->printf(format: "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 format: "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(log_fd: get_logging_fd());
545 if (fd >= 0)
546 save_backtrace(fd, err: fd_errno.get_errno(), string_sp: description_sp, is_create: true);
547 return fd;
548 } else {
549 return ::socket(domain: domain, type: type, protocol: protocol);
550 }
551}
552
553// socketpair() interpose function
554extern "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: domain, type: type, protocol: protocol, fds: 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(log_fd: get_logging_fd());
569 if (fds[0] >= 0)
570 save_backtrace(fd: fds[0], err: err_errno.get_errno(), string_sp: description_sp, is_create: true);
571 if (fds[1] >= 0)
572 save_backtrace(fd: fds[1], err: err_errno.get_errno(), string_sp: description_sp, is_create: true);
573 return err;
574 } else {
575 return socketpair(domain: domain, type: type, protocol: protocol, fds: fds);
576 }
577}
578
579// open() interpose function
580extern "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(file: path, oflag: oflag, mode);
588 description_sp->printf(
589 format: "pid=%i: open (path = '%s', oflag = %i, mode = %i) -> fd=%i", pid,
590 path, oflag, mode, fd);
591 } else {
592 fd = ::open(file: path, oflag: oflag);
593 description_sp->printf(format: "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(log_fd: get_logging_fd());
600 if (fd >= 0)
601 save_backtrace(fd, err: fd_errno.get_errno(), string_sp: description_sp, is_create: true);
602 return fd;
603 } else {
604 return ::open(file: path, oflag: oflag, mode);
605 }
606}
607
608// open$NOCANCEL() interpose function
609extern "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(log_fd: get_logging_fd());
621 if (fd >= 0)
622 save_backtrace(fd, err: fd_errno.get_errno(), string_sp: description_sp, is_create: true);
623 return fd;
624 } else {
625 return ::open$NOCANCEL(path, oflag, mode);
626 }
627}
628
629// __open_extended() interpose function
630extern "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(log_fd: get_logging_fd());
644 if (fd >= 0)
645 save_backtrace(fd, err: fd_errno.get_errno(), string_sp: description_sp, is_create: true);
646 return fd;
647 } else {
648 return ::__open_extended(path, oflag, uid, gid, mode, fsacl);
649 }
650}
651
652// kqueue() interpose function
653extern "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(log_fd: get_logging_fd());
662 if (fd >= 0)
663 save_backtrace(fd, err: fd_errno.get_errno(), string_sp: description_sp, is_create: true);
664 return fd;
665 } else {
666 return ::kqueue();
667 }
668}
669
670// shm_open() interpose function
671extern "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(name: path, oflag: oflag, mode: 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(log_fd: get_logging_fd());
682 if (fd >= 0)
683 save_backtrace(fd, err: fd_errno.get_errno(), string_sp: description_sp, is_create: true);
684 return fd;
685 } else {
686 return ::shm_open(name: path, oflag: oflag, mode: mode);
687 }
688}
689
690// accept() interpose function
691extern "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(fd: socket, addr: address, addr_len: 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(log_fd: get_logging_fd());
702 if (fd >= 0)
703 save_backtrace(fd, err: fd_errno.get_errno(), string_sp: description_sp, is_create: true);
704 return fd;
705 } else {
706 return ::accept(fd: socket, addr: address, addr_len: address_len);
707 }
708}
709
710// accept$NOCANCEL() interpose function
711extern "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(log_fd: get_logging_fd());
723 if (fd >= 0)
724 save_backtrace(fd, err: fd_errno.get_errno(), string_sp: description_sp, is_create: true);
725 return fd;
726 } else {
727 return ::accept$NOCANCEL(socket, address, address_len);
728 }
729}
730
731// dup() interpose function
732extern "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(fd: 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(log_fd: get_logging_fd());
742 if (fd >= 0)
743 save_backtrace(fd, err: fd_errno.get_errno(), string_sp: description_sp, is_create: true);
744 return fd;
745 } else {
746 return ::dup(fd: fd2);
747 }
748}
749
750// dup2() interpose function
751extern "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(x: 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(fd: fd2, err: 0, string_sp: dup2_close_description_sp, is_create: false);
764
765 const int fd = ::dup2(fd: fd1, fd2: 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(log_fd: get_logging_fd());
771
772 if (fd >= 0)
773 save_backtrace(fd, err: fd_errno.get_errno(), string_sp: description_sp, is_create: true);
774 return fd;
775 } else {
776 return ::dup2(fd: fd1, fd2: fd2);
777 }
778}
779
780// close() interpose function
781extern "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: fd);
786 NegativeErrorErrno err_errno(err);
787 StringSP description_sp(new String);
788 if (err == -1)
789 description_sp->printf(format: "pid=%i: close (fd=%i) => %i errno = %i (%s))",
790 pid, fd, err, err_errno.get_errno(),
791 strerror(errnum: err_errno.get_errno()));
792 else
793 description_sp->printf(format: "pid=%i: close (fd=%i) => %i", pid, fd, err);
794 if (g_log_all_calls)
795 description_sp->log(log_fd: get_logging_fd());
796
797 if (err == 0) {
798 if (fd >= 0)
799 save_backtrace(fd, err, string_sp: description_sp, is_create: false);
800 } else if (err == -1) {
801 if (err_errno.get_errno() == EBADF && fd != -1) {
802 backtrace_error(format: "close (fd=%d) resulted in EBADF:\n", fd);
803
804 FDEventMap::iterator pos = g_fd_event_map.find(x: fd);
805 if (pos != g_fd_event_map.end()) {
806 log(log_fd: get_logging_fd(), event: pos->second.back().get(),
807 format: "\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: fd);
815 }
816}
817
818// close$NOCANCEL() interpose function
819extern "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 format: "pid=%i: close$NOCANCEL (fd=%i) => %i errno = %i (%s))", pid, fd, err,
829 err_errno.get_errno(), strerror(errnum: err_errno.get_errno()));
830 else
831 description_sp->printf(format: "pid=%i: close$NOCANCEL (fd=%i) => %i", pid, fd,
832 err);
833 if (g_log_all_calls)
834 description_sp->log(log_fd: get_logging_fd());
835
836 if (err == 0) {
837 if (fd >= 0)
838 save_backtrace(fd, err, string_sp: description_sp, is_create: false);
839 } else if (err == -1) {
840 if (err_errno.get_errno() == EBADF && fd != -1) {
841 backtrace_error(format: "close$NOCANCEL (fd=%d) resulted in EBADF\n:", fd);
842
843 FDEventMap::iterator pos = g_fd_event_map.find(x: fd);
844 if (pos != g_fd_event_map.end()) {
845 log(log_fd: get_logging_fd(), event: pos->second.back().get(),
846 format: "\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
858extern "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(pipedes: 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(log_fd: get_logging_fd());
870 if (fds[0] >= 0)
871 save_backtrace(fd: fds[0], err: saved_errno, string_sp: description_sp, is_create: true);
872 if (fds[1] >= 0)
873 save_backtrace(fd: fds[1], err: saved_errno, string_sp: description_sp, is_create: true);
874 errno = saved_errno;
875 return err;
876 } else {
877 return pipe(pipedes: 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
890extern "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(x: fd);
897 log_to_fd(log_fd, format: "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, format: "error: no file descriptor events found for fd=%i\n",
905 fd);
906 }
907 } else {
908 log_to_fd(log_fd, format: "error: fd event mutex is locked...\n");
909 }
910 }
911}
912
913// Interposing
914// FD creation routines
915DYLD_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 routines
929DYLD_INTERPOSE(close$__interposed__, close);
930DYLD_INTERPOSE(close$NOCANCEL$__interposed__, close$NOCANCEL);
931
932} // namespace fd_interposing
933

source code of lldb/examples/interposing/darwin/fd_interposing/FDInterposing.cpp