From 6a441255f65449b8751288a35a06c209ec56de14 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Wed, 23 Apr 2025 19:42:20 +0200 Subject: [PATCH] Fix deadlock in ks_log Fix race condition between select and fread which can cause fread to block forever. Use non-blocking and unbuffered read(2) instead. Fixes: https://github.com/signalwire/libks/issues/226 --- src/ks_log.c | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/ks_log.c b/src/ks_log.c index ae785e57..870052e6 100644 --- a/src/ks_log.c +++ b/src/ks_log.c @@ -108,35 +108,21 @@ static void __set_blocking(int fd, int blocking) static ks_bool_t wakeup_stdout() { char procpath[256]; - FILE *fp = NULL; - fd_set set; - struct timeval timeout; + int fd; ks_bool_t done_reading = KS_FALSE; char buf[1024]; size_t skipped = 0; snprintf(procpath, sizeof(procpath), "/proc/%d/fd/%d", getpid(), fileno(stdout)); - if ((fp = fopen(procpath, "r")) == NULL) { + if ((fd = open(procpath, O_RDONLY)) < 0) { g_wakeup_stdout_fails++; return KS_FALSE; } - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - while (!done_reading) { - FD_ZERO(&set); - FD_SET(fileno(fp), &set); - - if (select(fileno(fp) + 1, &set, NULL, NULL, &timeout) != 1) { - done_reading = KS_TRUE; - } else { - size_t consumed = fread(buf, 1, sizeof(buf), fp); - skipped += consumed; - done_reading = consumed < sizeof(buf); - } - } - fclose(fp); + __set_blocking(fd, 0); + // read until EAGAIN + while (read(fd, buf, sizeof(buf)) > 0); + close(fd); g_wakeup_stdout_successes++; return KS_TRUE;