From 9bd91a3751cfb01c9499a5ee7080349b9c8f0f65 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Tue, 23 Aug 2022 18:18:13 -0700 Subject: [PATCH] Fix overflows in HAVE_XSYNC timestamp handling Also, port to platforms lacking CLOCK_MONOTONIC and int64_t, and use 0 more consistently to represent missing timestamps. * src/xterm.h (struct x_display_info): Omit server_time_monotonic_p and server_time_offset if !HAVE_CLOCK_GETTIME since they are unused in that case. * src/xterm.h (struct x_display_info, struct x_output): * src/xterm.c (x_sync_get_monotonic_time) (x_sync_current_monotonic_time, x_sync_note_frame_times): Use int_fast64_t instead of int64_t as POSIX doesn't guarantee the latter. Similarly for uint_fast64_t. (x_sync_get_monotonic_time, x_sync_current_monotonic_time) (x_sync_note_frame_times, x_display_set_last_user_time): Check for integer overflow in time arithmetic. (CLOCK_MONOTONIC): Define to CLOCK_REALTIME if absent. (x_sync_current_monotonic_time): Check for clock_gettime failure and fall back on CLOCK_REALTIME if CLOCK_MONOTONIC does not work, which POSIX allows. (x_sync_current_monotonic_time, x_sync_note_frame_times) (x_display_set_last_user_time): Use 0 more consistently to represent missing timestamps. --- src/xterm.c | 76 +++++++++++++++++++++++++++++++---------------------- src/xterm.h | 10 +++---- 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/src/xterm.c b/src/xterm.c index fb4c0c74db3..5a36aa03029 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -6714,9 +6714,9 @@ x_if_event (Display *dpy, XEvent *event_return, server timestamp TIMESTAMP. Return 0 if the necessary information is not available. */ -static uint64_t +static uint_fast64_t x_sync_get_monotonic_time (struct x_display_info *dpyinfo, - uint64_t timestamp) + uint_fast64_t timestamp) { if (dpyinfo->server_time_monotonic_p) return timestamp; @@ -6725,19 +6725,29 @@ x_sync_get_monotonic_time (struct x_display_info *dpyinfo, if (!dpyinfo->server_time_offset) return 0; - return timestamp - dpyinfo->server_time_offset; + uint_fast64_t t; + return (INT_SUBTRACT_WRAPV (timestamp, dpyinfo->server_time_offset, &t) + ? 0 : t); } +# ifndef CLOCK_MONOTONIC +# define CLOCK_MONOTONIC CLOCK_REALTIME +# endif + /* Return the current monotonic time in the same format as a - high-resolution server timestamp. */ + high-resolution server timestamp, or 0 if not available. */ -static uint64_t +static uint_fast64_t x_sync_current_monotonic_time (void) { struct timespec time; - - clock_gettime (CLOCK_MONOTONIC, &time); - return time.tv_sec * 1000000 + time.tv_nsec / 1000; + uint_fast64_t t; + return (((clock_gettime (CLOCK_MONOTONIC, &time) != 0 + && (CLOCK_MONOTONIC == CLOCK_REALTIME + || clock_gettime (CLOCK_REALTIME, &time) != 0)) + || INT_MULTIPLY_WRAPV (time.tv_sec, 1000000, &t) + || INT_ADD_WRAPV (t, time.tv_nsec / 1000, &t)) + ? 0 : t); } /* Decode a _NET_WM_FRAME_DRAWN message and calculate the time it took @@ -6747,7 +6757,7 @@ static void x_sync_note_frame_times (struct x_display_info *dpyinfo, struct frame *f, XEvent *event) { - uint64_t low, high, time; + uint_fast64_t low, high, time; struct x_output *output; low = event->xclient.data.l[2]; @@ -6756,12 +6766,16 @@ x_sync_note_frame_times (struct x_display_info *dpyinfo, time = x_sync_get_monotonic_time (dpyinfo, low | (high << 32)); - if (time) - output->last_frame_time = time - output->temp_frame_time; + if (!time || !output->temp_frame_time + || INT_SUBTRACT_WRAPV (time, output->temp_frame_time, + &output->last_frame_time)) + output->last_frame_time = 0; #ifdef FRAME_DEBUG - fprintf (stderr, "Drawing the last frame took: %lu ms (%lu)\n", - output->last_frame_time / 1000, time); + uint_fast64_t last_frame_ms = output->last_frame_time / 1000; + fprintf (stderr, + "Drawing the last frame took: %"PRIuFAST64" ms (%"PRIuFAST64")\n", + last_frame_ms, time); #endif } @@ -6891,22 +6905,16 @@ x_sync_update_begin (struct frame *f) static void x_sync_trigger_fence (struct frame *f, XSyncValue value) { - uint64_t n, low, high, idx; - /* Sync fences aren't supported by the X server. */ if (FRAME_DISPLAY_INFO (f)->xsync_major < 3 || (FRAME_DISPLAY_INFO (f)->xsync_major == 3 && FRAME_DISPLAY_INFO (f)->xsync_minor < 1)) return; - low = XSyncValueLow32 (value); - high = XSyncValueHigh32 (value); - - n = low | (high << 32); - idx = (n / 4) % 2; + bool idx = !! (XSyncValueLow32 (value) & 4); #ifdef FRAME_DEBUG - fprintf (stderr, "Triggering synchronization fence: %lu\n", idx); + fprintf (stderr, "Triggering synchronization fence: %d\n", idx); #endif XSyncTriggerFence (FRAME_X_DISPLAY (f), @@ -7600,9 +7608,6 @@ x_display_set_last_user_time (struct x_display_info *dpyinfo, Time time, #ifndef USE_GTK struct frame *focus_frame; Time old_time; -#if defined HAVE_XSYNC && defined HAVE_CLOCK_GETTIME - uint64_t monotonic_time; -#endif focus_frame = dpyinfo->x_focus_frame; old_time = dpyinfo->last_user_time; @@ -7620,19 +7625,26 @@ x_display_set_last_user_time (struct x_display_info *dpyinfo, Time time, { /* See if the current CLOCK_MONOTONIC time is reasonably close to the X server time. */ - monotonic_time = x_sync_current_monotonic_time (); + uint_fast64_t monotonic_time = x_sync_current_monotonic_time (); + uint_fast64_t monotonic_ms = monotonic_time / 1000; + int_fast64_t diff_ms; - if (time * 1000 > monotonic_time - 500 * 1000 - && time * 1000 < monotonic_time + 500 * 1000) - dpyinfo->server_time_monotonic_p = true; - else + dpyinfo->server_time_monotonic_p + = (monotonic_time != 0 + && !INT_SUBTRACT_WRAPV (time, monotonic_ms, &diff_ms) + && -500 < diff_ms && diff_ms < 500); + + if (!dpyinfo->server_time_monotonic_p) { /* Compute an offset that can be subtracted from the server time to estimate the monotonic time on the X server. */ - dpyinfo->server_time_monotonic_p = false; - dpyinfo->server_time_offset - = ((int64_t) time * 1000) - monotonic_time; + if (!monotonic_time + || INT_MULTIPLY_WRAPV (time, 1000, &dpyinfo->server_time_offset) + || INT_SUBTRACT_WRAPV (dpyinfo->server_time_offset, + monotonic_time, + &dpyinfo->server_time_offset)) + dpyinfo->server_time_offset = 0; } } #endif diff --git a/src/xterm.h b/src/xterm.h index 9d9675428ff..3654c3d5db1 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -828,14 +828,14 @@ struct x_display_info drag-and-drop emulation. */ Time pending_dnd_time; -#if defined HAVE_XSYNC && !defined USE_GTK +#if defined HAVE_XSYNC && !defined USE_GTK && defined HAVE_CLOCK_GETTIME /* Whether or not the server time is probably the same as "clock_gettime (CLOCK_MONOTONIC, ...)". */ bool server_time_monotonic_p; /* The time difference between the X server clock and the monotonic - clock. */ - int64_t server_time_offset; + clock, or 0 if unknown (FIXME: what if the difference is zero?). */ + int_fast64_t server_time_offset; #endif }; @@ -1131,10 +1131,10 @@ struct x_output bool_bf use_vsync_p : 1; /* The time (in microseconds) it took to draw the last frame. */ - uint64_t last_frame_time; + uint_fast64_t last_frame_time; /* A temporary time used to calculate that value. */ - uint64_t temp_frame_time; + uint_fast64_t temp_frame_time; #ifdef HAVE_XSYNCTRIGGERFENCE /* An array of two sync fences that are triggered in order after a -- 2.39.2