bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#32951: emacs Lock-Up-Crash Bug report and patch


From: Scott Corley
Subject: bug#32951: emacs Lock-Up-Crash Bug report and patch
Date: Fri, 5 Oct 2018 21:30:25 -0500

If an emacs window exceeds 255 lines, it causes an unsigned char overflow in
scroll.c:do_direct_scrolling

The effect of this overflow is that do_direct_scrolling enters
an infinite loop.
This causes emacs to lock up in an unrecoverable way, and the process has to
be killed.

This bug can be reproduced in a number of ways:

1. Open up emacs on a very large display and expand the emacs window so that
   it is larger than 255 lines. Do some editing, add text to the window,
   enough that do_direct_scrolling is invoked.

   For example, opening and editing emacs/INSTALL will eventually cause
   the lockup. You may need to navigate through the file and/or delete and
   edit some lines.
   Once do_direct_scrolling function is invoked, it will enter an infinite
   loop and lock up emacs.

   (new 43" monitor in portrait mode is how I first encountered this bug).
   This happens in "-nw" mode and in X display mode.

2. If you don't have a large display, open up an X windows instance of
   emacs and decrease the font size until the window has more than 255
   lines. Follow the editing example in (1) above, and emacs will lock up.

3. You can also reproduce in '-nw' mode on a normal size display by opening
   up a terminal window with a very small font size, enough to have more
   than 255 vertical lines.

I can reproduce the bug using all of these methods on
these and similar systems:

   Red Hat Enterprise Linux Server Release 7.3 (Maipo)
   Intel Xeon CPU E5-2667 V3
   GNU Emacs 24.3.1
   GNU Emacs 27.0.50 (built from master)

   macOS Mojave
   2013 MacBook Pro
   Intel Core i7
   GNU Emacs 25.3.1

Cause of unsigned char overflow:

struct matrix_elt, used by do_direct_scrolling, in scroll.c, has three fields
that are declared as unsigned char. It looks like they were previously
declared as signed char, then changed to unsigned char, perhaps to fix a
previous incarnation of this overflow.

The fields in question are 'insertcount', 'deletecount' and 'writecount'.

The patch below changes these fields to 'int'. The alloction of 'matrix_elt'
is a small memory footprint
(see allocation in scroll.c function 'scrolling_1')
I can't see a justification for keeping these fields 'unsigned char'.

On most systems, it is actually a challenge to create an emacs window larger
than 255 lines, which is why I'm guessing this overflow hasn't come up before
(I couldn't find bug report mentioning it). However, on a system with a 4k
display in portrait mode, it is pretty easy to accidentally create a window
this large.

For example, if I am using a tmux display with reasonably-sized pane on a
4k monitor in portrait mode, and I happen to zoom the tmux pane to full
screen, the emacs window will now be larger than 255 lines, emacs will enter
an infinite loop, and the process will need to be killed.

The overflow was confirmed by attaching to a crashed/looping emacs
process with gdb, and stepping through the code, on different operating
systems and machines.

Changelog:

2018-10-05   Scott Corley <address@hidden>
* scroll.c (struct matrix_elt): change unsigned char fields to int
  fixes overflow lockup crash with window sizes > 255 lines.

Here is the patch:

---
 src/scroll.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/scroll.c b/src/scroll.c
index a29f2d37f5..240005b4e3 100644
--- a/src/scroll.c
+++ b/src/scroll.c
@@ -41,13 +41,13 @@ struct matrix_elt
     int deletecost;
     /* Number of inserts so far in this run of inserts,
        for the cost in insertcost.  */
-    unsigned char insertcount;
+    int insertcount;
     /* Number of deletes so far in this run of deletes,
        for the cost in deletecost.  */
-    unsigned char deletecount;
+    int deletecount;
     /* Number of writes so far since the last insert
        or delete for the cost in writecost. */
-    unsigned char writecount;
+    int writecount;
   };

 static void do_direct_scrolling (struct frame *,
--

Thanks,
Scott

reply via email to

[Prev in Thread] Current Thread [Next in Thread]