]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix two crashes when a display connection is lost
authorPo Lu <luangruo@yahoo.com>
Tue, 7 Jun 2022 01:26:15 +0000 (09:26 +0800)
committerPo Lu <luangruo@yahoo.com>
Tue, 7 Jun 2022 01:26:15 +0000 (09:26 +0800)
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
src/lisp.h
src/xterm.c
src/xterm.h

index c3be1dc12c81a6efae211d655db4f8b9cabd4fe7..d4d4a6cfdd83e045bf44c9a11d8407e977e00038 100644 (file)
@@ -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;
 }
index ff6f0aaf54d9b14a6c5b83be78d6fb3f09510aa7..499bacc3303af422bba6e9f4bb27c12e252fa328 100644 (file)
@@ -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;
index fa7e285425cb12cef9ec68002fc4b125ea85281f..ee396f38cc70e845e8f9589dac99954839a2e7a0 100644 (file)
@@ -21781,13 +21781,12 @@ x_text_icon (struct frame *f, const char *icon_name)
   return false;
 }
 \f
-#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);
index 22c6b5517664b1c4e997f206aa7e401811479ccf..c6be30d73ef9e3f98c31d6d809555163dbea6f36 100644 (file)
@@ -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);