This is an automated email from the git hooks/post-receive script.

git pushed a commit to reference refs/pull/114/head
in repository efl.

View the commit online.

commit 4ad5ea4b9a9a52d164b0c67696f64ea53252171f
Author: Cedric BAIL <[email protected]>
AuthorDate: Fri Aug 14 11:10:13 2026 -0600

    fix(evas_ector_gl): do not free the span page after its context is gone
    
    The shared span page takes its texture from the GL context's texture
    pool, but was released in eng_engine_free(), which runs after the
    engine's outputs - and with them that context and its pool - are gone.
    evas_gl_common_texture_free() then walked pool structures that had
    already been freed.
    
    It only ever crashed sometimes, which is why it was easy to miss.
    Running the test suite under meson made it deterministic:
    MALLOC_PERTURB_ is set there, so the freed pool no longer happened to
    still look valid, and the teardown segfaulted every time in
    eina_rectangle_pool_free().
    
    Tell span_page_free() whether the texture may still be touched. The
    engine passes whether a GL context can still be found; by the time it
    is tearing down there is none, and the pool has already taken the
    texture with it. The page also remembers which context its texture
    belongs to, so a page carried across a context change drops the stale
    texture rather than reusing or freeing it.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
 .../evas/engines/gl_generic/Evas_Engine_GL_Generic.h     |  2 +-
 src/modules/evas/engines/gl_generic/evas_ector_gl_span.h | 14 ++++++++++++--
 .../evas/engines/gl_generic/evas_ector_gl_span_shader.c  | 16 ++++++++++++++--
 src/modules/evas/engines/gl_generic/evas_engine.c        | 10 +++++++++-
 4 files changed, 36 insertions(+), 6 deletions(-)

diff --git a/src/modules/evas/engines/gl_generic/Evas_Engine_GL_Generic.h b/src/modules/evas/engines/gl_generic/Evas_Engine_GL_Generic.h
index a9ff26c6d7..131e444f23 100644
--- a/src/modules/evas/engines/gl_generic/Evas_Engine_GL_Generic.h
+++ b/src/modules/evas/engines/gl_generic/Evas_Engine_GL_Generic.h
@@ -15,7 +15,7 @@
  * wayland_egl.  The definitions live in evas_ector_gl_span_shader.c. */
 struct _Span_Page;
 struct _Span_Page *span_page_new(void);
-void               span_page_free(struct _Span_Page *page);
+void               span_page_free(struct _Span_Page *page, Eina_Bool release_tex);
 
 typedef struct _Render_Engine_GL_Generic Render_Engine_GL_Generic;
 typedef struct _Render_Output_GL_Generic Render_Output_GL_Generic;
diff --git a/src/modules/evas/engines/gl_generic/evas_ector_gl_span.h b/src/modules/evas/engines/gl_generic/evas_ector_gl_span.h
index 7ae1d44c67..349dd287b9 100644
--- a/src/modules/evas/engines/gl_generic/evas_ector_gl_span.h
+++ b/src/modules/evas/engines/gl_generic/evas_ector_gl_span.h
@@ -355,6 +355,7 @@ Eina_Bool span_collector_supports_composite(Efl_Gfx_Vg_Composite_Method comp_met
 typedef struct _Span_Page
 {
    void     *evas_tex;   /* Evas_GL_Texture *; NULL until first upload */
+   void     *gc;         /* the Evas_Engine_GL_Context evas_tex belongs to */
    int       w, h;       /* logical size currently allocated */
    uint32_t  prev_hash;  /* combined hash of the last uploaded pass */
 } Span_Page;
@@ -362,8 +363,17 @@ typedef struct _Span_Page
 /** Allocate an empty page.  No GL resource is taken until first upload. */
 Span_Page *span_page_new(void);
 
-/** Free @p page and its GL texture.  Must be called from the GL thread. */
-void span_page_free(Span_Page *page);
+/**
+ * Free @p page.
+ *
+ * @p release_tex says whether the GL texture may still be touched.  It comes
+ * from the context's texture pool, so once that context is gone the pool has
+ * already freed it and releasing it again is a use-after-free.  Pass
+ * EINA_FALSE when tearing down after the context has been destroyed.
+ *
+ * Must be called from the GL thread.
+ */
+void span_page_free(Span_Page *page, Eina_Bool release_tex);
 
 /**
  * Pack the span rows of every collector in @p fills and @p strokes into
diff --git a/src/modules/evas/engines/gl_generic/evas_ector_gl_span_shader.c b/src/modules/evas/engines/gl_generic/evas_ector_gl_span_shader.c
index a755beb20b..5e36772987 100644
--- a/src/modules/evas/engines/gl_generic/evas_ector_gl_span_shader.c
+++ b/src/modules/evas/engines/gl_generic/evas_ector_gl_span_shader.c
@@ -1282,10 +1282,10 @@ span_page_new(void)
 }
 
 void
-span_page_free(Span_Page *page)
+span_page_free(Span_Page *page, Eina_Bool release_tex)
 {
    if (!page) return;
-   if (page->evas_tex)
+   if (page->evas_tex && release_tex)
      evas_gl_common_texture_free((Evas_GL_Texture *)page->evas_tex, EINA_TRUE);
    free(page);
 }
@@ -1340,6 +1340,17 @@ _span_page_ensure(Span_Page *page, Evas_Engine_GL_Context *gc, int w, int h)
    RGBA_Image  *im;
    Image_Entry *ie;
 
+   /* A texture from a previous context is not ours to free - that context's
+    * pool already did - but it must not be used either. */
+   if (t && page->gc != gc)
+     {
+        t = NULL;
+        page->evas_tex  = NULL;
+        page->gc        = NULL;
+        page->w = page->h = 0;
+        page->prev_hash = 0;
+     }
+
    if (t && page->w >= w && page->h >= h) return EINA_TRUE;
 
    /* Grow to at least what is asked, never shrink. */
@@ -1365,6 +1376,7 @@ _span_page_ensure(Span_Page *page, Evas_Engine_GL_Context *gc, int w, int h)
    if (!t) return EINA_FALSE;
 
    page->evas_tex = t;
+   page->gc = gc;
    page->w = w;
    page->h = h;
    return EINA_TRUE;
diff --git a/src/modules/evas/engines/gl_generic/evas_engine.c b/src/modules/evas/engines/gl_generic/evas_engine.c
index b1db92b579..99d9263029 100644
--- a/src/modules/evas/engines/gl_generic/evas_engine.c
+++ b/src/modules/evas/engines/gl_generic/evas_engine.c
@@ -200,7 +200,15 @@ eng_engine_free(void *engine)
 
    generic_cache_destroy(e->software.surface_cache);
 
-   if (e->span_page) { span_page_free(e->span_page); e->span_page = NULL; }
+   if (e->span_page)
+     {
+        /* The page's texture came from a GL context's texture pool.  Outputs
+         * are gone by the time the engine is freed - the loop below shouts if
+         * they are not - and that pool went with them, taking the texture. */
+        span_page_free(e->span_page,
+                       gl_generic_context_find(engine, EINA_FALSE) != NULL);
+        e->span_page = NULL;
+     }
    if (e->grad_atlas) span_grad_atlas_free(e->grad_atlas);
 
    EINA_LIST_FREE(e->software.outputs, output)

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to