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

perf record: Fix event fd races

The write call may set errno which is problematic if occurring in a
function also setting errno. Save and restore errno around the write
call.

done_fd may be used after close, clear it as part of the close and check
its validity in the signal handler.

Suggested-by: <gthelen@google.com>
Reviewed-by: Leo Yan <leo.yan@linaro.org>
Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Anand K Mistry <amistry@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: https://lore.kernel.org/r/20221024011024.462518-1-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
304f0a2f f1bdebbb

+25 -16
+25 -16
tools/perf/builtin-record.c
··· 649 649 static volatile int signr = -1; 650 650 static volatile int child_finished; 651 651 #ifdef HAVE_EVENTFD_SUPPORT 652 - static int done_fd = -1; 652 + static volatile int done_fd = -1; 653 653 #endif 654 654 655 655 static void sig_handler(int sig) ··· 661 661 662 662 done = 1; 663 663 #ifdef HAVE_EVENTFD_SUPPORT 664 - { 665 - u64 tmp = 1; 666 - /* 667 - * It is possible for this signal handler to run after done is checked 668 - * in the main loop, but before the perf counter fds are polled. If this 669 - * happens, the poll() will continue to wait even though done is set, 670 - * and will only break out if either another signal is received, or the 671 - * counters are ready for read. To ensure the poll() doesn't sleep when 672 - * done is set, use an eventfd (done_fd) to wake up the poll(). 673 - */ 674 - if (write(done_fd, &tmp, sizeof(tmp)) < 0) 675 - pr_err("failed to signal wakeup fd, error: %m\n"); 676 - } 664 + if (done_fd >= 0) { 665 + u64 tmp = 1; 666 + int orig_errno = errno; 667 + 668 + /* 669 + * It is possible for this signal handler to run after done is 670 + * checked in the main loop, but before the perf counter fds are 671 + * polled. If this happens, the poll() will continue to wait 672 + * even though done is set, and will only break out if either 673 + * another signal is received, or the counters are ready for 674 + * read. To ensure the poll() doesn't sleep when done is set, 675 + * use an eventfd (done_fd) to wake up the poll(). 676 + */ 677 + if (write(done_fd, &tmp, sizeof(tmp)) < 0) 678 + pr_err("failed to signal wakeup fd, error: %m\n"); 679 + 680 + errno = orig_errno; 681 + } 677 682 #endif // HAVE_EVENTFD_SUPPORT 678 683 } 679 684 ··· 2839 2834 2840 2835 out_delete_session: 2841 2836 #ifdef HAVE_EVENTFD_SUPPORT 2842 - if (done_fd >= 0) 2843 - close(done_fd); 2837 + if (done_fd >= 0) { 2838 + fd = done_fd; 2839 + done_fd = -1; 2840 + 2841 + close(fd); 2842 + } 2844 2843 #endif 2845 2844 zstd_fini(&session->zstd_data); 2846 2845 perf_session__delete(session);