avfilter/vf_drawtext: don't double-free glyph that has been cached in tree
authorMichael Niedermayer <michael@niedermayer.cc>
Sun, 17 May 2026 11:45:38 +0000 (13:45 +0200)
committerMichael Niedermayer <michael@niedermayer.cc>
Sun, 14 Jun 2026 02:59:05 +0000 (04:59 +0200)
Reproducer:
  ffmpeg -f lavfi -i color=s=640x120:d=1 \
      -vf "drawtext=fontfile=/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf:\
text='FFmpeg':fontsize=109:fontcolor=white:x=20:y=35" \
      -frames:v 1 out.png

Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
(cherry picked from commit c51789b052dc87e741fa3d02afe7ee83ed370373)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
libavfilter/vf_drawtext.c

index 3335a19736de14a2f33aa593b5fc9ef759e4f56c..ec344cd6ac4f89044f47e27585ff47d2e621de9f 100644 (file)
@@ -735,11 +735,13 @@ static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code, in
     FT_Vector shift;
     struct AVTreeNode *node = NULL;
     int ret = 0;
+    int cached = 0;
 
     /* get glyph */
     dummy.code = code;
     dummy.fontsize = s->fontsize;
     glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);
+    cached = !!glyph;
     if (!glyph) {
         if (FT_Load_Glyph(s->face, code, s->ft_load_flags)) {
             return AVERROR(EINVAL);
@@ -756,11 +758,12 @@ static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code, in
             goto error;
         }
         if (s->borderw) {
-            glyph->border_glyph = glyph->glyph;
-            if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0)) {
+            FT_Glyph tmp = glyph->glyph;
+            if (FT_Glyph_StrokeBorder(&tmp, s->stroker, 0, 0)) {
                 ret = AVERROR_EXTERNAL;
                 goto error;
             }
+            glyph->border_glyph = tmp;
         }
         /* measure text height to calculate text_height (or the maximum text height) */
         FT_Glyph_Get_CBox(glyph->glyph, FT_GLYPH_BBOX_SUBPIXELS, &glyph->bbox);
@@ -771,13 +774,15 @@ static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code, in
             goto error;
         }
         av_tree_insert(&s->glyphs, glyph, glyph_cmp, &node);
+        cached = 1;
     } else {
         if (s->borderw && !glyph->border_glyph) {
-            glyph->border_glyph = glyph->glyph;
-            if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0)) {
+            FT_Glyph tmp = glyph->glyph;
+            if (FT_Glyph_StrokeBorder(&tmp, s->stroker, 0, 0)) {
                 ret = AVERROR_EXTERNAL;
                 goto error;
             }
+            glyph->border_glyph = tmp;
         }
     }
 
@@ -816,10 +821,13 @@ static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code, in
     return 0;
 
 error:
-    if (glyph && glyph->glyph)
-        FT_Done_Glyph(glyph->glyph);
-
-    av_freep(&glyph);
+    if (glyph && !cached) {
+        if (glyph->border_glyph && glyph->border_glyph != glyph->glyph)
+            FT_Done_Glyph(glyph->border_glyph);
+        if (glyph->glyph)
+            FT_Done_Glyph(glyph->glyph);
+        av_freep(&glyph);
+    }
     av_freep(&node);
     return ret;
 }