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