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