Commit 6cdf21b3 for guacamole.apache.org

commit 6cdf21b32156496bf2ffa883fc4659d89fd37d08
Author: Michael Jumper <mjumper@apache.org>
Date:   Fri Sep 25 18:08:50 2026 -0700

    GUACAMOLE-2118: Recheck guac_flag flags after pthread_cond_t timeout (per POSIX manual).

diff --git a/src/libguac/flag.c b/src/libguac/flag.c
index 39afb5a0..e03337b1 100644
--- a/src/libguac/flag.c
+++ b/src/libguac/flag.c
@@ -30,15 +30,26 @@
  */
 #define NANOS_PER_SECOND 1000000000L

+/**
+ * The clock to use scheduling the timeout of guac_flag's timed wait. As the
+ * realtime clock is subject to time changes, this will be the system-wide
+ * monotonic clock except on platforms that entirely lack a monotonic clock.
+ *
+ * @see guac_flag_timedwait_and_lock()
+ */
+#ifdef CLOCK_MONOTONIC
+#define GUAC_FLAG_CLOCK CLOCK_MONOTONIC
+#else
+#define GUAC_FLAG_CLOCK CLOCK_REALTIME
+#endif
+
 void guac_flag_init(guac_flag* event_flag) {

-    /* The condition used by guac_flag to signal changes in its
-     * value must be safe to share between processes, and must use the
-     * system-wide monotonic clock (not the realtime clock, which is subject to
-     * time changes) */
+    /* The condition guac_flag uses to signal changes must be shareable between
+     * processes, and must use the system-wide monotonic clock where possible */
     pthread_condattr_t cond_attr;
     pthread_condattr_init(&cond_attr);
-    pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
+    pthread_condattr_setclock(&cond_attr, GUAC_FLAG_CLOCK);
     pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
     pthread_cond_init(&event_flag->value_changed, &cond_attr);

@@ -151,20 +162,29 @@ int guac_flag_timedwait_and_lock(guac_flag* event_flag,
     }

     struct timespec ts_timeout;
-    clock_gettime(CLOCK_MONOTONIC, &ts_timeout);
+    clock_gettime(GUAC_FLAG_CLOCK, &ts_timeout);

-    uint64_t nsec_timeout = msec_timeout * 1000000 + ts_timeout.tv_nsec;
+    uint64_t nsec_timeout = (uint64_t) msec_timeout * 1000000 + ts_timeout.tv_nsec;
     ts_timeout.tv_sec += nsec_timeout / NANOS_PER_SECOND;
     ts_timeout.tv_nsec = nsec_timeout % NANOS_PER_SECOND;

     /* Continue waiting until at least one of the desired flags has been set */
     while (!(event_flag->value & flags)) {

-        /* Wait for any change to any flags, failing if a timeout occurs */
+        /* Wait for any change to any flags, failing if no such change occurs
+         * before the timeout */
         if (pthread_cond_timedwait(&event_flag->value_changed,
                     &event_flag->value_mutex, &ts_timeout)) {
-            guac_flag_unlock(event_flag);
-            return 0;
+
+            /* NOTE: Per POSIX, the predicate must be rechecked even after the
+             * timeout lapses (unavoidable race between the expiration of the
+             * timeout and the flag value change) */
+            int retval = event_flag->value & flags;
+            if (!retval)
+                guac_flag_unlock(event_flag);
+
+            return retval;
+
         }

     }