Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf dso: Use lock annotations to fix asan deadlock

dso__list_del with address sanitizer and/or reference count checking
will call dso__put that can call dso__data_close reentrantly trying to
lock the dso__data_open_lock and deadlocking. Switch from pthread
mutexes to perf's mutex so that lock checking is performed in debug
builds. Add lock annotations that diagnosed the problem. Release the
dso__data_open_lock around the dso__put to avoid the deadlock.

Change the declaration of dso__data_get_fd to return a boolean,
indicating the fd is valid and the lock is held, to make it compatible
with the thread safety annotations as a try lock.

Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250318043151.137973-3-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Ian Rogers and committed by
Namhyung Kim
5ac22c35 c5ebf3a2

+66 -43
+2 -2
tools/perf/tests/dso-data.c
··· 106 106 /* move it from util/dso.c for compatibility */ 107 107 static int dso__data_fd(struct dso *dso, struct machine *machine) 108 108 { 109 - int fd = dso__data_get_fd(dso, machine); 109 + int fd = -1; 110 110 111 - if (fd >= 0) 111 + if (dso__data_get_fd(dso, machine, &fd)) 112 112 dso__data_put_fd(dso); 113 113 114 114 return fd;
+47 -27
tools/perf/util/dso.c
··· 493 493 /* 494 494 * Global list of open DSOs and the counter. 495 495 */ 496 + struct mutex _dso__data_open_lock; 496 497 static LIST_HEAD(dso__data_open); 497 - static long dso__data_open_cnt; 498 - static pthread_mutex_t dso__data_open_lock = PTHREAD_MUTEX_INITIALIZER; 498 + static long dso__data_open_cnt GUARDED_BY(_dso__data_open_lock); 499 499 500 - static void dso__list_add(struct dso *dso) 500 + static void dso__data_open_lock_init(void) 501 + { 502 + mutex_init(&_dso__data_open_lock); 503 + } 504 + 505 + static struct mutex *dso__data_open_lock(void) LOCK_RETURNED(_dso__data_open_lock) 506 + { 507 + static pthread_once_t data_open_lock_once = PTHREAD_ONCE_INIT; 508 + 509 + pthread_once(&data_open_lock_once, dso__data_open_lock_init); 510 + 511 + return &_dso__data_open_lock; 512 + } 513 + 514 + static void dso__list_add(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) 501 515 { 502 516 list_add_tail(&dso__data(dso)->open_entry, &dso__data_open); 503 517 #ifdef REFCNT_CHECKING ··· 522 508 dso__data_open_cnt++; 523 509 } 524 510 525 - static void dso__list_del(struct dso *dso) 511 + static void dso__list_del(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) 526 512 { 527 513 list_del_init(&dso__data(dso)->open_entry); 528 514 #ifdef REFCNT_CHECKING 515 + mutex_unlock(dso__data_open_lock()); 529 516 dso__put(dso__data(dso)->dso); 517 + mutex_lock(dso__data_open_lock()); 530 518 #endif 531 519 WARN_ONCE(dso__data_open_cnt <= 0, 532 520 "DSO data fd counter out of bounds."); ··· 537 521 538 522 static void close_first_dso(void); 539 523 540 - static int do_open(char *name) 524 + static int do_open(char *name) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) 541 525 { 542 526 int fd; 543 527 char sbuf[STRERR_BUFSIZE]; ··· 564 548 } 565 549 566 550 static int __open_dso(struct dso *dso, struct machine *machine) 551 + EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) 567 552 { 568 553 int fd = -EINVAL; 569 554 char *root_dir = (char *)""; ··· 630 613 * list/count of open DSO objects. 631 614 */ 632 615 static int open_dso(struct dso *dso, struct machine *machine) 616 + EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) 633 617 { 634 618 int fd; 635 619 struct nscookie nsc; ··· 656 638 return fd; 657 639 } 658 640 659 - static void close_data_fd(struct dso *dso) 641 + static void close_data_fd(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) 660 642 { 661 643 if (dso__data(dso)->fd >= 0) { 662 644 close(dso__data(dso)->fd); ··· 673 655 * Close @dso's data file descriptor and updates 674 656 * list/count of open DSO objects. 675 657 */ 676 - static void close_dso(struct dso *dso) 658 + static void close_dso(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) 677 659 { 678 660 close_data_fd(dso); 679 661 } 680 662 681 - static void close_first_dso(void) 663 + static void close_first_dso(void) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) 682 664 { 683 665 struct dso_data *dso_data; 684 666 struct dso *dso; ··· 723 705 fd_limit = 0; 724 706 } 725 707 726 - static bool may_cache_fd(void) 708 + static bool may_cache_fd(void) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) 727 709 { 728 710 if (!fd_limit) 729 711 fd_limit = get_fd_limit(); ··· 739 721 * for opened dso file descriptors. The limit is half 740 722 * of the RLIMIT_NOFILE files opened. 741 723 */ 742 - static void check_data_close(void) 724 + static void check_data_close(void) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) 743 725 { 744 726 bool cache_fd = may_cache_fd(); 745 727 ··· 755 737 */ 756 738 void dso__data_close(struct dso *dso) 757 739 { 758 - pthread_mutex_lock(&dso__data_open_lock); 740 + mutex_lock(dso__data_open_lock()); 759 741 close_dso(dso); 760 - pthread_mutex_unlock(&dso__data_open_lock); 742 + mutex_unlock(dso__data_open_lock()); 761 743 } 762 744 763 745 static void try_to_open_dso(struct dso *dso, struct machine *machine) 746 + EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) 764 747 { 765 748 enum dso_binary_type binary_type_data[] = { 766 749 DSO_BINARY_TYPE__BUILD_ID_CACHE, ··· 803 784 * returns file descriptor. It should be paired with 804 785 * dso__data_put_fd() if it returns non-negative value. 805 786 */ 806 - int dso__data_get_fd(struct dso *dso, struct machine *machine) 787 + bool dso__data_get_fd(struct dso *dso, struct machine *machine, int *fd) 807 788 { 789 + *fd = -1; 808 790 if (dso__data(dso)->status == DSO_DATA_STATUS_ERROR) 809 - return -1; 791 + return false; 810 792 811 - if (pthread_mutex_lock(&dso__data_open_lock) < 0) 812 - return -1; 793 + mutex_lock(dso__data_open_lock()); 813 794 814 795 try_to_open_dso(dso, machine); 815 796 816 - if (dso__data(dso)->fd < 0) 817 - pthread_mutex_unlock(&dso__data_open_lock); 797 + *fd = dso__data(dso)->fd; 798 + if (*fd >= 0) 799 + return true; 818 800 819 - return dso__data(dso)->fd; 801 + mutex_unlock(dso__data_open_lock()); 802 + return false; 820 803 } 821 804 822 805 void dso__data_put_fd(struct dso *dso __maybe_unused) 823 806 { 824 - pthread_mutex_unlock(&dso__data_open_lock); 807 + mutex_unlock(dso__data_open_lock()); 825 808 } 826 809 827 810 bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by) ··· 975 954 { 976 955 ssize_t ret; 977 956 978 - pthread_mutex_lock(&dso__data_open_lock); 957 + mutex_lock(dso__data_open_lock()); 979 958 980 959 /* 981 960 * dso__data(dso)->fd might be closed if other thread opened another ··· 991 970 992 971 ret = pread(dso__data(dso)->fd, data, DSO__DATA_CACHE_SIZE, offset); 993 972 out: 994 - pthread_mutex_unlock(&dso__data_open_lock); 973 + mutex_unlock(dso__data_open_lock()); 995 974 return ret; 996 975 } 997 976 ··· 1099 1078 struct stat st; 1100 1079 char sbuf[STRERR_BUFSIZE]; 1101 1080 1102 - pthread_mutex_lock(&dso__data_open_lock); 1081 + mutex_lock(dso__data_open_lock()); 1103 1082 1104 1083 /* 1105 1084 * dso__data(dso)->fd might be closed if other thread opened another ··· 1123 1102 dso__data(dso)->file_size = st.st_size; 1124 1103 1125 1104 out: 1126 - pthread_mutex_unlock(&dso__data_open_lock); 1105 + mutex_unlock(dso__data_open_lock()); 1127 1106 return ret; 1128 1107 } 1129 1108 ··· 1632 1611 1633 1612 enum dso_type dso__type(struct dso *dso, struct machine *machine) 1634 1613 { 1635 - int fd; 1614 + int fd = -1; 1636 1615 enum dso_type type = DSO__TYPE_UNKNOWN; 1637 1616 1638 - fd = dso__data_get_fd(dso, machine); 1639 - if (fd >= 0) { 1617 + if (dso__data_get_fd(dso, machine, &fd)) { 1640 1618 type = dso__type_fd(fd); 1641 1619 dso__data_put_fd(dso); 1642 1620 }
+9 -6
tools/perf/util/dso.h
··· 232 232 char name[]; 233 233 }; 234 234 235 + extern struct mutex _dso__data_open_lock; 236 + 235 237 /* dso__for_each_symbol - iterate over the symbols of given type 236 238 * 237 239 * @dso: the 'struct dso *' in which symbols are iterated ··· 655 653 int dso__name_len(const struct dso *dso); 656 654 657 655 struct dso *dso__get(struct dso *dso); 658 - void dso__put(struct dso *dso); 656 + void dso__put(struct dso *dso) LOCKS_EXCLUDED(_dso__data_open_lock); 659 657 660 658 static inline void __dso__zput(struct dso **dso) 661 659 { ··· 735 733 * The current usage of the dso__data_* interface is as follows: 736 734 * 737 735 * Get DSO's fd: 738 - * int fd = dso__data_get_fd(dso, machine); 739 - * if (fd >= 0) { 736 + * int fd; 737 + * if (dso__data_get_fd(dso, machine, &fd)) { 740 738 * USE 'fd' SOMEHOW 741 739 * dso__data_put_fd(dso); 742 740 * } ··· 758 756 * 759 757 * TODO 760 758 */ 761 - int dso__data_get_fd(struct dso *dso, struct machine *machine); 762 - void dso__data_put_fd(struct dso *dso); 763 - void dso__data_close(struct dso *dso); 759 + bool dso__data_get_fd(struct dso *dso, struct machine *machine, int *fd) 760 + EXCLUSIVE_TRYLOCK_FUNCTION(true, _dso__data_open_lock); 761 + void dso__data_put_fd(struct dso *dso) UNLOCK_FUNCTION(_dso__data_open_lock); 762 + void dso__data_close(struct dso *dso) LOCKS_EXCLUDED(_dso__data_open_lock); 764 763 765 764 int dso__data_file_size(struct dso *dso, struct machine *machine); 766 765 off_t dso__data_size(struct dso *dso, struct machine *machine);
+8 -8
tools/perf/util/unwind-libunwind-local.c
··· 330 330 int ret, fd; 331 331 332 332 if (dso__data(dso)->eh_frame_hdr_offset == 0) { 333 - fd = dso__data_get_fd(dso, ui->machine); 334 - if (fd < 0) 333 + if (!dso__data_get_fd(dso, ui->machine, &fd)) 335 334 return -EINVAL; 336 335 337 336 /* Check the .eh_frame section for unwinding info */ ··· 371 372 * has to be pointed by symsrc_filename 372 373 */ 373 374 if (ofs == 0) { 374 - fd = dso__data_get_fd(dso, machine); 375 - if (fd >= 0) { 375 + if (dso__data_get_fd(dso, machine, &fd) { 376 376 ofs = elf_section_offset(fd, ".debug_frame"); 377 377 dso__data_put_fd(dso); 378 378 } ··· 483 485 /* Check the .debug_frame section for unwinding info */ 484 486 if (ret < 0 && 485 487 !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) { 486 - int fd = dso__data_get_fd(dso, ui->machine); 487 - int is_exec = elf_is_exec(fd, dso__name(dso)); 488 + int fd; 488 489 u64 start = map__start(map); 489 - unw_word_t base = is_exec ? 0 : start; 490 + unw_word_t base = start; 490 491 const char *symfile; 491 492 492 - if (fd >= 0) 493 + if (dso__data_get_fd(dso, ui->machine, &fd)) { 494 + if (elf_is_exec(fd, dso__name(dso))) 495 + base = 0; 493 496 dso__data_put_fd(dso); 497 + } 494 498 495 499 symfile = dso__symsrc_filename(dso) ?: dso__name(dso); 496 500