Subject: bug#40317: 27.0.90; Reverting a buffer that visits C file signals an error

From: Jeff Norden <[email-masked]>
Date: Thu, 17 Sep 2020 20:21:47 -0500

I came across this by searching for args-out-of-range bugs.  I recently found
a bug in forward-comment (which I'll post separately) that was causing
out-of-range errors for me, and I wondered if forward-comment might be
relevant to other issues.  It isn't in this case, but I think I did find the
source of the problem.

The function c-after-change (in cc-mode.el) was changed between 26.3 and 27.1,
to handle more cases where the before and/or after change functions get called
multiple times.  The function now begins (line numbers are from the current
master version) with:

1993   ;; Note: c-just-done-before-change is nil, t, or 'whole-buffer.
1994   (unless (c-called-from-text-property-change-p)
1995     (save-restriction
1996       (widen)
1997       (unless c-just-done-before-change
1998         (c-before-change (point-min) (point-max)))
1999       (unless (eq c-just-done-before-change t)
2000         (setq beg (point-min)
2001               end (point-max)
2002               old-len (- end beg)
2003               c-new-BEG (point-min)
2004               c-new-END (point-max)))
2005       (setq c-just-done-before-change nil)))
2006 
2007   ;; (c-new-BEG c-new-END) will be the region to fontify.  It may become
2008   ;; larger than (beg end).
2009   (setq c-new-END (- (+ c-new-END (- end beg)) old-len))

It looks like it is now possible for the last line above, which increments
c-new-END, to run even if c-new-END has been set to the after-change value
of point-max.  That will make c-new-END point past the end of the buffer.

---

In the backtrace from March,

>   c-after-change(214738 215088 0)

indicates that 350 bytes have been added to the buffer, although not at end.
If point-max was 216785 (which would be the value if the buffer-length was
actually 216784), then line-2009 would set c-new-END to 216785+350 = 217135,
which would then be used by c-after-change-mark-abnormal-strings when calling
parse-partial-sexp.  This fits the args-out-of-range error in the backtrace.

---

A simple fix would be to change line-2009 to 

   (setq c-new-END (min (- (+ c-new-END (- end beg)) old-len) (point-max)))

But, maybe the third 'unless' could be changed to 'if' instead, with the
increment as the else.  I don't know if c-new-END might need to be
incremented when c-called-from-text-property-change-p is true.

Unfortunately, I can't figure out how to trigger this bug myself.  If you want
to be 100% sure about it, you might try adding

  (if (> c-new-END (point-max))
    (error "c-new-END is too big! %d > %d" c-new-END (point-max)))

right after line-2009 and see if it raises an error before it gets to
parse-partial-sexp.

Hope this helps,
-Jeff