From ef17247fe9cab2d59e470daad24314c868248b0a Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Mon, 4 Feb 2019 19:42:33 +0200 Subject: [PATCH] Avoid segfaults due to image cache being cleared during redisplay * src/xdisp.c (redisplay_internal): Set the inhibit_clear_image_cache flag of a frame while its windows are being redisplayed, and reset the flag after the call top update_frame returns. * src/image.c (clear_image_cache): Do nothing if the frame's inhibit_clear_image_cache flag is set. (Bug#34256) * src/frame.h (struct frame): New flag inhibit_clear_image_cache. --- src/frame.h | 4 ++++ src/image.c | 2 +- src/xdisp.c | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/frame.h b/src/frame.h index ab3efdfa926..b7cbdd95571 100644 --- a/src/frame.h +++ b/src/frame.h @@ -413,6 +413,10 @@ struct frame /* Non-zero if this frame's faces need to be recomputed. */ bool_bf face_change : 1; + /* Non-zero if this frame's image cache cannot be freed because the + frame is in the process of being redisplayed. */ + bool_bf inhibit_clear_image_cache : 1; + /* Bitfield area ends here. */ /* This frame's change stamp, set the last time window change diff --git a/src/image.c b/src/image.c index 57bbf3cdb93..642bf671520 100644 --- a/src/image.c +++ b/src/image.c @@ -1554,7 +1554,7 @@ clear_image_cache (struct frame *f, Lisp_Object filter) { struct image_cache *c = FRAME_IMAGE_CACHE (f); - if (c) + if (c && !f->inhibit_clear_image_cache) { ptrdiff_t i, nfreed = 0; diff --git a/src/xdisp.c b/src/xdisp.c index b5034b513e6..0bffaeb60bc 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -14431,7 +14431,17 @@ redisplay_internal (void) FRAME_TERMINAL (f)->condemn_scroll_bars_hook (f); if (FRAME_VISIBLE_P (f) && !FRAME_OBSCURED_P (f)) - redisplay_windows (FRAME_ROOT_WINDOW (f)); + { + + /* Don't allow freeing images for this frame as long + as the frame's update wasn't completed. This + prevents crashes when some Lisp that runs from + the various hooks or font-lock decides to clear + the frame's image cache, when the images in that + cache are referenced by the desired matrix. */ + f->inhibit_clear_image_cache = true; + redisplay_windows (FRAME_ROOT_WINDOW (f)); + } /* Remember that the invisible frames need to be redisplayed next time they're visible. */ else if (!REDISPLAY_SOME_P ()) @@ -14512,6 +14522,7 @@ redisplay_internal (void) pending |= update_frame (f, false, false); f->cursor_type_changed = false; f->updated_p = true; + f->inhibit_clear_image_cache = false; } } } @@ -14539,6 +14550,7 @@ redisplay_internal (void) } else if (FRAME_VISIBLE_P (sf) && !FRAME_OBSCURED_P (sf)) { + sf->inhibit_clear_image_cache = true; displayed_buffer = XBUFFER (XWINDOW (selected_window)->contents); /* Use list_of_error, not Qerror, so that we catch only errors and don't run the debugger. */ @@ -14594,6 +14606,7 @@ redisplay_internal (void) XWINDOW (selected_window)->must_be_updated_p = true; pending = update_frame (sf, false, false); sf->cursor_type_changed = false; + sf->inhibit_clear_image_cache = false; } /* We may have called echo_area_display at the top of this -- 2.39.5