From 2267b48cac3c8e8a834b4faaa5390f2ad6a54281 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Tue, 7 Jun 2022 09:26:15 +0800 Subject: [PATCH] Fix two crashes when a display connection is lost This fixes errors caused by invalid error traps being left on the error handler stack if an IO error causes a non-local exit out of the protected code, and another crash caused by delete_frame trying to read async input. * src/eval.c (unwind_to_catch, push_handler_nosignal): Save and restore the X error handler stack. * src/lisp.h (struct handler): [HAVE_X_WINDOWS]: New field `x_error_handler_depth'. * src/xterm.c (struct x_error_message_stack): Make string a regular string. (x_unwind_errors_to): New function. (x_error_catcher, x_catch_errors_with_handler) (x_uncatch_errors_after_check, x_uncatch_errors): Update the stack depth. (x_check_errors): Stop manually unwinding since unwind_to_catch now does that for us. (x_had_errors_p, x_clear_errors): Update for new type of `string'. (x_connection_closed): Block input between just before delete_frame to when the terminal is unlinked. * src/xterm.h: Update prototypes. --- src/eval.c | 10 +++++++++ src/lisp.h | 4 ++++ src/xterm.c | 65 +++++++++++++++++++++++++++++++++++++++++------------ src/xterm.h | 2 ++ 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/eval.c b/src/eval.c index c3be1dc12c8..d4d4a6cfdd8 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1251,6 +1251,13 @@ unwind_to_catch (struct handler *catch, enum nonlocal_exit type, set_poll_suppress_count (catch->poll_suppress_count); unblock_input_to (catch->interrupt_input_blocked); +#ifdef HAVE_X_WINDOWS + /* Restore the X error handler stack. This is important because + otherwise a display disconnect won't unwind the stack of error + traps to the right depth. */ + x_unwind_errors_to (catch->x_error_handler_depth); +#endif + do { /* Unwind the specpdl stack, and then restore the proper set of @@ -1625,6 +1632,9 @@ push_handler_nosignal (Lisp_Object tag_ch_val, enum handlertype handlertype) c->act_rec = get_act_rec (current_thread); c->poll_suppress_count = poll_suppress_count; c->interrupt_input_blocked = interrupt_input_blocked; +#ifdef HAVE_X_WINDOWS + c->x_error_handler_depth = x_error_message_count; +#endif handlerlist = c; return c; } diff --git a/src/lisp.h b/src/lisp.h index ff6f0aaf54d..499bacc3303 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3631,6 +3631,10 @@ struct handler struct bc_frame *act_rec; int poll_suppress_count; int interrupt_input_blocked; + +#ifdef HAVE_X_WINDOWS + int x_error_handler_depth; +#endif }; extern Lisp_Object memory_signal_data; diff --git a/src/xterm.c b/src/xterm.c index fa7e285425c..ee396f38cc7 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -21781,13 +21781,12 @@ x_text_icon (struct frame *f, const char *icon_name) return false; } -#define X_ERROR_MESSAGE_SIZE 200 struct x_error_message_stack { - /* Buffer containing the error message of any error that was - generated. */ - char string[X_ERROR_MESSAGE_SIZE]; + /* Pointer to the error message of any error that was generated, or + NULL. */ + char *string; /* The display this error handler applies to. */ Display *dpy; @@ -21817,6 +21816,9 @@ struct x_error_message_stack placed before 2006. */ static struct x_error_message_stack *x_error_message; +/* The amount of items (depth) in that stack. */ +int x_error_message_count; + static struct x_error_message_stack * x_find_error_handler (Display *dpy, XErrorEvent *event) { @@ -21837,6 +21839,17 @@ x_find_error_handler (Display *dpy, XErrorEvent *event) return NULL; } +void +x_unwind_errors_to (int depth) +{ + while (x_error_message_count > depth) + /* This is safe to call because we check whether or not + x_error_message->dpy is still alive before calling XSync. */ + x_uncatch_errors (); +} + +#define X_ERROR_MESSAGE_SIZE 200 + /* An X error handler which stores the error message in the first applicable handler in the x_error_message stack. This is called from *x_error_handler if an x_catch_errors for DISPLAY is in @@ -21846,8 +21859,15 @@ static void x_error_catcher (Display *display, XErrorEvent *event, struct x_error_message_stack *stack) { + char buf[X_ERROR_MESSAGE_SIZE]; + XGetErrorText (display, event->error_code, - stack->string, X_ERROR_MESSAGE_SIZE); + buf, X_ERROR_MESSAGE_SIZE); + + if (stack->string) + xfree (stack->string); + + stack->string = xstrdup (buf); if (stack->handler) stack->handler (display, event, stack->string, @@ -21875,15 +21895,17 @@ void x_catch_errors_with_handler (Display *dpy, x_special_error_handler handler, void *handler_data) { - struct x_error_message_stack *data = xmalloc (sizeof *data); + struct x_error_message_stack *data; + data = xzalloc (sizeof *data); data->dpy = dpy; - data->string[0] = 0; data->handler = handler; data->handler_data = handler_data; data->prev = x_error_message; data->first_request = NextRequest (dpy); x_error_message = data; + + ++x_error_message_count; } void @@ -21907,6 +21929,9 @@ x_uncatch_errors_after_check (void) block_input (); tmp = x_error_message; x_error_message = x_error_message->prev; + --x_error_message_count; + if (tmp->string) + xfree (tmp->string); xfree (tmp); unblock_input (); } @@ -21942,6 +21967,9 @@ x_uncatch_errors (void) tmp = x_error_message; x_error_message = x_error_message->prev; + --x_error_message_count; + if (tmp->string) + xfree (tmp->string); xfree (tmp); unblock_input (); } @@ -21953,7 +21981,7 @@ x_uncatch_errors (void) void x_check_errors (Display *dpy, const char *format) { - char string[X_ERROR_MESSAGE_SIZE]; + char *string; /* This shouldn't happen, since x_check_errors should be called immediately inside an x_catch_errors block. */ @@ -21968,11 +21996,11 @@ x_check_errors (Display *dpy, const char *format) > x_error_message->first_request)) XSync (dpy, False); - if (x_error_message->string[0]) + if (x_error_message->string) { - memcpy (string, x_error_message->string, - X_ERROR_MESSAGE_SIZE); - x_uncatch_errors (); + string = alloca (strlen (x_error_message->string) + 1); + strcpy (string, x_error_message->string); + error (format, string); } } @@ -21995,7 +22023,7 @@ x_had_errors_p (Display *dpy) > x_error_message->first_request)) XSync (dpy, False); - return x_error_message->string[0] != 0; + return x_error_message->string; } /* Forget about any errors we have had, since we did x_catch_errors on @@ -22009,7 +22037,8 @@ x_clear_errors (Display *dpy) if (dpy != x_error_message->dpy) emacs_abort (); - x_error_message->string[0] = 0; + xfree (x_error_message->string); + x_error_message->string = NULL; } #if false @@ -22142,6 +22171,12 @@ x_connection_closed (Display *dpy, const char *error_message, bool ioerror) dpyinfo->display = 0; } + /* delete_frame can still try to read async input (even though we + tell pass `noelisp'), because looking up the `delete-before' + parameter calls Fassq which then calls maybe_quit. So block + input while deleting frames. */ + block_input (); + /* First delete frames whose mini-buffers are on frames that are on the dead display. */ FOR_EACH_FRAME (tail, frame) @@ -22206,6 +22241,8 @@ For details, see etc/PROBLEMS.\n", } } + unblock_input (); + if (terminal_list == 0) { fprintf (stderr, "%s\n", error_msg); diff --git a/src/xterm.h b/src/xterm.h index 22c6b551766..c6be30d73ef 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -1396,6 +1396,7 @@ extern void x_catch_errors_with_handler (Display *, x_special_error_handler, extern void x_check_errors (Display *, const char *) ATTRIBUTE_FORMAT_PRINTF (2, 0); extern bool x_had_errors_p (Display *); +extern void x_unwind_errors_to (int); extern void x_uncatch_errors (void); extern void x_uncatch_errors_after_check (void); extern void x_clear_errors (Display *); @@ -1615,6 +1616,7 @@ extern bool x_dnd_waiting_for_finish; extern struct frame *x_dnd_frame; extern struct frame *x_dnd_finish_frame; extern unsigned x_dnd_unsupported_event_level; +extern int x_error_message_count; #ifdef HAVE_XINPUT2 extern struct xi_device_t *xi_device_from_id (struct x_display_info *, int); -- 2.39.2