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