]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Bug 492678 - SIGALRM race condition (sighandler called after timer disarmed
authorPaul Floyd <pjfloyd@wanadoo.fr>
Sat, 25 Jan 2025 13:54:08 +0000 (14:54 +0100)
committerPaul Floyd <pjfloyd@wanadoo.fr>
Sat, 25 Jan 2025 16:07:57 +0000 (17:07 +0100)
.gitignore
coregrind/m_syswrap/syswrap-freebsd.c
coregrind/m_syswrap/syswrap-generic.c
coregrind/m_syswrap/syswrap-linux.c
coregrind/m_syswrap/syswrap-solaris.c
none/tests/Makefile.am
none/tests/bug492678.c [new file with mode: 0644]
none/tests/bug492678.stderr.exp [new file with mode: 0644]
none/tests/bug492678.vgtest [new file with mode: 0644]
none/tests/timer_delete.c [new file with mode: 0644]
none/tests/timer_delete.stderr.exp [new file with mode: 0644]

index d44246b8bcd71f90ad28eb6cdb742398228c690e..4a6874e5c1cbfe2606f2bbbb9ea6910b3357bb65 100644 (file)
 /none/tests/bug129866
 /none/tests/bug234814
 /none/tests/bug491394
+/none/tests/bug492678
 /none/tests/closeall
 /none/tests/coolo_sigaction
 /none/tests/coolo_strlen
 /none/tests/thread-exits
 /none/tests/threaded-fork
 /none/tests/threadederrno
+/none/tests/timer_delete
 /none/tests/timestamp
 /none/tests/tls
 /none/tests/track-fds-exec-children
index d358c90eb05104ec231c19233b49623ed8b2a1c4..3397249383c04f0535d3321fa2680e9adadad5a3 100644 (file)
@@ -2411,6 +2411,7 @@ PRE(sys_timer_delete)
 {
    PRINT("sys_timer_delete( %#" FMT_REGWORD "x )", ARG1);
    PRE_REG_READ1(long, "timer_delete", vki_timer_t, timerid);
+   *flags |= SfPollAfter;
 }
 
 // SYS_ktimer_settime   237
index a74b8f1d216ecc2df0a9f2148a84a1504a554aa8..c281021a9f039b05410ec4fa39822a0798543be8 100644 (file)
@@ -2959,6 +2959,14 @@ PRE(sys_setitimer)
                          &(value->it_interval));
       PRE_timeval_READ( "setitimer(&value->it_value)",
                          &(value->it_value));
+      // "Setting it_value to 0 disables a timer"
+      // poll for signals in that case
+      if (ML_(safe_to_deref)(value, sizeof(*value))) {
+         if (value->it_value.tv_sec == 0 &&
+             value->it_value.tv_usec == 0) {
+            *flags |= SfPollAfter;
+         }
+      }
    }
    if (ARG3 != (Addr)NULL) {
       struct vki_itimerval *ovalue = (struct vki_itimerval*)(Addr)ARG3;
index e8978b5bc1c5c9d84336996da43b3dff93512aaa..83af91344ee5812d0e3f3515e495bd0299526e35 100644 (file)
@@ -3244,6 +3244,7 @@ PRE(sys_timer_delete)
 {
    PRINT("sys_timer_delete( %#" FMT_REGWORD "x )", ARG1);
    PRE_REG_READ1(long, "timer_delete", vki_timer_t, timerid);
+   *flags |= SfPollAfter;
 }
 
 /* ---------------------------------------------------------------------
index 104a809d3dc9c2095994cee74e1073bfcf290102..9abeb85001779e91651a525288ba266598398a84 100644 (file)
@@ -8531,6 +8531,7 @@ PRE(sys_timer_delete)
    /* int timer_delete(timer_t timerid); */
    PRINT("sys_timer_delete ( %ld )", SARG1);
    PRE_REG_READ1(long, "timer_delete", vki_timer_t, timerid);
+   *flags |= SfPollAfter;
 }
 
 PRE(sys_timer_settime)
index 3dc8762514c89a9f29f38e66481d440e4c82a636..2fa43587e285b5dd0c0c4433b4647e18ca2501a2 100644 (file)
@@ -103,6 +103,7 @@ EXTRA_DIST = \
        bug129866.vgtest bug129866.stderr.exp bug129866.stdout.exp \
        bug234814.vgtest bug234814.stderr.exp bug234814.stdout.exp \
        bug491394.vgtest bug491394.stderr.exp \
+       bug492678.vgtest bug492678.stderr.exp \
        closeall.stderr.exp closeall.vgtest \
        cmdline0.stderr.exp cmdline0.stdout.exp cmdline0.vgtest \
        cmdline1.stderr.exp cmdline1.stdout.exp cmdline1.vgtest \
@@ -241,6 +242,7 @@ EXTRA_DIST = \
        threaded-fork.stderr.exp threaded-fork.stdout.exp threaded-fork.vgtest \
        threadederrno.stderr.exp threadederrno.stdout.exp \
        threadederrno.vgtest \
+       timer_delete.vgtest timer_delete.stderr.exp \
        timestamp.stderr.exp timestamp.vgtest \
        tls.vgtest tls.stderr.exp tls.stdout.exp  \
        track-fds-exec-children.vgtest track-fds-exec-children.stderr.exp \
@@ -267,7 +269,7 @@ check_PROGRAMS = \
        args \
        async-sigs \
        bitfield1 \
-       bug129866 bug234814 \
+       bug129866 bug234814 bug492678\
        closeall coolo_strlen \
        discard exec-sigmask execve faultstatus fcntl_setown \
        fdleak_cmsg fdleak_creat fdleak_dup fdleak_dup2 \
@@ -300,6 +302,7 @@ check_PROGRAMS = \
        thread-exits \
        threaded-fork \
        threadederrno \
+       timer_delete \
        timestamp \
        tls \
        tls.so \
@@ -439,6 +442,7 @@ thread_exits_LDADD  = -lpthread
 threaded_fork_LDADD    = -lpthread
 threadederrno_CFLAGS   = $(AM_CFLAGS)
 threadederrno_LDADD    = -lpthread
+timer_delete_LDADD      = -lrt
 timestamp_CFLAGS       = ${AM_CFLAGS} @FLAG_W_NO_USE_AFTER_FREE@
 tls_SOURCES            = tls.c tls2.c
 tls_DEPENDENCIES       = tls.so tls2.so
diff --git a/none/tests/bug492678.c b/none/tests/bug492678.c
new file mode 100644 (file)
index 0000000..6e4ca80
--- /dev/null
@@ -0,0 +1,104 @@
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#define CHECK_RES(res)                                                                                                 \
+    if (res == -1)                                                                                                     \
+    {                                                                                                                  \
+        fprintf(stderr, "unexpected error at line %d: %s", __LINE__, strerror(errno));                                 \
+        exit(EXIT_FAILURE);                                                                                            \
+    }
+
+static int tries = 0;
+
+static void sigalrm_handler(int signum)
+{
+    (void)signum;
+    // no need to do anything for the experiment
+}
+
+// using this handler in place of SIG_DFL to monitor when the "default" handler is called
+static void sigalrm_default_handler(int signum)
+{
+    (void)signum;
+    fprintf(stderr,
+            "ERROR: unreachable code reached: sigalarm timer was disarmed, but still received SIGALRM (race condition "
+            "tried %d times)\n",
+            tries);
+    exit(EXIT_FAILURE);
+}
+
+void sigalrm_timer_destroy(void)
+{
+    int res;
+
+    // REMOVE TIMER
+    const struct itimerval zero = {
+        .it_interval =
+            {
+                .tv_sec = 0,
+                .tv_usec = 0,
+            },
+        .it_value =
+            {
+                .tv_sec = 0,
+                .tv_usec = 0,
+            },
+    };
+    res = setitimer(ITIMER_REAL, &zero, NULL);
+    CHECK_RES(res)
+
+    // AND THEN REMOVE SIGNAL HANDLER
+    struct sigaction sigact;
+    sigact.sa_flags = 0;
+    sigact.sa_handler = sigalrm_default_handler; // using this handler in place of SIG_DFL to monitor when the "default"
+                                                 // handler is called
+    res = sigemptyset(&sigact.sa_mask);
+    CHECK_RES(res)
+    res = sigaction(SIGALRM, &sigact, NULL);
+    CHECK_RES(res)
+}
+
+void sigalrm_timer_setup(const struct timeval *period)
+{
+    int res;
+
+    // SIGNAL
+    struct sigaction sigact;
+    sigact.sa_flags = 0;
+    sigact.sa_handler = sigalrm_handler;
+    res = sigemptyset(&sigact.sa_mask);
+    CHECK_RES(res)
+    res = sigaction(SIGALRM, &sigact, NULL);
+    CHECK_RES(res)
+
+    // TIMER
+    const struct itimerval timer_val = {
+        .it_interval = *period,
+        .it_value = *period,
+    };
+    res = setitimer(ITIMER_REAL, &timer_val, NULL);
+    CHECK_RES(res);
+}
+
+static void try_race_condition(void)
+{
+    ++tries;
+    const struct timeval clk_period = {
+        .tv_sec = 0,
+        .tv_usec = 1,
+    };
+    sigalrm_timer_setup(&clk_period);
+    sigalrm_timer_destroy();
+}
+
+int main(void)
+{
+    for (int i = 0; i < 10000; ++i)
+    {
+        try_race_condition();
+    }
+}
diff --git a/none/tests/bug492678.stderr.exp b/none/tests/bug492678.stderr.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/none/tests/bug492678.vgtest b/none/tests/bug492678.vgtest
new file mode 100644 (file)
index 0000000..39ff276
--- /dev/null
@@ -0,0 +1,3 @@
+prog: bug492678
+vgopts: -q
+
diff --git a/none/tests/timer_delete.c b/none/tests/timer_delete.c
new file mode 100644 (file)
index 0000000..4a103d8
--- /dev/null
@@ -0,0 +1,102 @@
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+timer_t timerid;
+
+#define CHECK_RES(res)                                                                                                 \
+    if (res == -1)                                                                                                     \
+    {                                                                                                                  \
+        fprintf(stderr, "unexpected error at line %d: %s", __LINE__, strerror(errno));                                 \
+        exit(EXIT_FAILURE);                                                                                            \
+    }
+
+static int tries = 0;
+
+static void sigalrm_handler(int signum)
+{
+    (void)signum;
+    // no need to do anything for the experiment
+}
+
+// using this handler in place of SIG_DFL to monitor when the "default" handler is called
+static void sigalrm_default_handler(int signum)
+{
+    (void)signum;
+    fprintf(stderr,
+            "ERROR: unreachable code reached: sigalarm timer was disarmed, but still received SIGALRM (race condition "
+            "tried %d times)\n",
+            tries);
+    exit(EXIT_FAILURE);
+}
+
+void sigalrm_timer_destroy(void)
+{
+    int res;
+
+    // REMOVE TIMER
+    res = timer_delete(timerid);
+    CHECK_RES(res)
+
+    // AND THEN REMOVE SIGNAL HANDLER
+    struct sigaction sigact;
+    sigact.sa_flags = 0;
+    sigact.sa_handler = sigalrm_default_handler; // using this handler in place of SIG_DFL to monitor when the "default"
+                                                 // handler is called
+    res = sigemptyset(&sigact.sa_mask);
+    CHECK_RES(res)
+    res = sigaction(SIGALRM, &sigact, NULL);
+    CHECK_RES(res)
+}
+
+void sigalrm_timer_setup(const struct timespec *period)
+{
+    int res;
+
+    // SIGNAL
+    struct sigaction sigact;
+    sigact.sa_flags = 0;
+    sigact.sa_handler = sigalrm_handler;
+    res = sigemptyset(&sigact.sa_mask);
+    CHECK_RES(res)
+    res = sigaction(SIGALRM, &sigact, NULL);
+    CHECK_RES(res)
+
+    // SIGEVENT
+    struct sigevent    sev;
+    sev.sigev_notify = SIGEV_SIGNAL;
+    sev.sigev_signo = SIGALRM;
+    sev.sigev_value.sival_ptr = &timerid;
+    res = timer_create(CLOCK_REALTIME, &sev, &timerid);
+    CHECK_RES(res);
+    
+    // TIMER
+    const struct itimerspec timer_val = {
+        .it_interval = *period,
+        .it_value = *period,
+    };
+    res = timer_settime(timerid, 0, &timer_val, NULL);
+    CHECK_RES(res);
+}
+
+static void try_race_condition(void)
+{
+    ++tries;
+    const struct timespec clk_period = {
+        .tv_sec = 0,
+        .tv_nsec = 20000
+    };
+    sigalrm_timer_setup(&clk_period);
+    sigalrm_timer_destroy();
+}
+
+int main(void)
+{
+    for (int i = 0; i < 10000; ++i)
+    {
+        try_race_condition();
+    }
+}
diff --git a/none/tests/timer_delete.stderr.exp b/none/tests/timer_delete.stderr.exp
new file mode 100644 (file)
index 0000000..e69de29