From 609e64053089bdb9b753af61f3a3448c21811cab Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 19 Oct 2023 07:54:55 -0400 Subject: [PATCH] LibPDF: Try harder to use a RAII object to restore state Follow-up to #21489. There, I made us use a RAII object. That's great, but if the embedded instruction stream pushes its own graphics state, then an early return would cause us to not process graphics state pop instructions in the embedded stream. To fix this, remember the graphics stack depth before entering the nested instruction stream, and explicitly shrink the stack back to that size upon exit. Enables us to render all pages of https://devstreaming-cdn.apple.com/videos/wwdc/2017/821kjtggolzxsv/821/821_get_started_with_display_p3.pdf without crashing. --- Userland/Libraries/LibPDF/Renderer.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibPDF/Renderer.cpp b/Userland/Libraries/LibPDF/Renderer.cpp index f3eaf0b160..01b509959f 100644 --- a/Userland/Libraries/LibPDF/Renderer.cpp +++ b/Userland/Libraries/LibPDF/Renderer.cpp @@ -646,20 +646,26 @@ RENDERER_HANDLER(paint_xobject) // Use a RAII object to restore the graphics state, to make sure it gets restored even if // a TRY(handle_operator()) causes us to exit the operators loop early. + // Explicitly resize stack size at the end so that if the recursive document contains + // `q q unsupportedop Q Q`, we undo the stack pushes from the inner `q q` even if + // `unsupportedop` terminates processing the inner instruction stream before `Q Q` + // would normally pop state. class ScopedState { public: ScopedState(Renderer& renderer) : m_renderer(renderer) + , m_starting_stack_depth(m_renderer.m_graphics_state_stack.size()) { MUST(m_renderer.handle_save_state({})); } ~ScopedState() { - MUST(m_renderer.handle_restore_state({})); + m_renderer.m_graphics_state_stack.shrink(m_starting_stack_depth); } private: Renderer& m_renderer; + size_t m_starting_stack_depth; }; ScopedState scoped_state { *this };