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