PR #20917 opened by Zhao Zhili (quink) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20917 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20917.patch
For GET_UTF8(val, GET_BYTE, ERROR), val has type of uint32_t, GET_BYTE must return an unsigned integer, otherwise signed extension happened due to val= (GET_BYTE), and GET_UTF8 went to the error path. This bug incidentally cancelled the bug where hb_buffer_add_utf8 was being called with incorrect argument, allowing drawtext to function correctly on x86 and macOS ARM, which defined char as signed. However, on Linux and Android ARM environments, because char is unsigned by default, GET_UTF8 now returns the correct return, which unexpectedly revealed issue #20906. >From 69f97b1d47137def27fda9adf0d6c8836f8cb29f Mon Sep 17 00:00:00 2001 From: Zhao Zhili <[email protected]> Date: Fri, 14 Nov 2025 16:23:10 +0800 Subject: [PATCH 1/3] avfilter/vf_drawtext: fix incorrect text length >From the doc of HarfBuzz, what hb_buffer_add_utf8 needs is the number of bytes, not Unicode character: hb_buffer_add_utf8(buf, text, strlen(text), 0, strlen(text)); Fix issue #20906. --- libavfilter/vf_drawtext.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c index 867536aa61..f5d0dc7d6b 100644 --- a/libavfilter/vf_drawtext.c +++ b/libavfilter/vf_drawtext.c @@ -1396,7 +1396,6 @@ static int measure_text(AVFilterContext *ctx, TextMetrics *metrics) DrawTextContext *s = ctx->priv; char *text = s->expanded_text.str; char *textdup = NULL, *start = NULL; - int num_chars = 0; int width64 = 0, w64 = 0; int cur_min_y64 = 0, first_max_y64 = -32000; int first_min_x64 = 32000, last_max_x64 = -32000; @@ -1459,7 +1458,7 @@ continue_on_failed2: TextLine *cur_line = &s->lines[line_count]; HarfbuzzData *hb = &cur_line->hb_data; cur_line->cluster_offset = line_offset; - ret = shape_text_hb(s, hb, start, num_chars); + ret = shape_text_hb(s, hb, start, p - start); if (ret != 0) { goto done; } @@ -1517,14 +1516,12 @@ continue_on_failed2: if (w64 > width64) { width64 = w64; } - num_chars = -1; start = p; ++line_count; line_offset = i + 1; } if (code == 0) break; - ++num_chars; } metrics->line_height64 = s->face->size->metrics.height; -- 2.49.1 >From 8024a765170a36895d78097dbd2ad85c34740eb5 Mon Sep 17 00:00:00 2001 From: Zhao Zhili <[email protected]> Date: Fri, 14 Nov 2025 16:53:07 +0800 Subject: [PATCH 2/3] avfilter/vf_drawtext: fix call GET_UTF8 with invalid argument For GET_UTF8(val, GET_BYTE, ERROR), val has type of uint32_t, GET_BYTE must return an unsigned integer, otherwise signed extension happened due to val= (GET_BYTE), and GET_UTF8 went to the error path. This bug incidentally cancelled the bug where hb_buffer_add_utf8 was being called with incorrect argument, allowing drawtext to function correctly on x86 and macOS ARM, which defined char as signed. However, on Linux and Android ARM environments, because char is unsigned by default, GET_UTF8 now returns the correct return, which unexpectedly revealed issue #20906. --- libavfilter/vf_drawtext.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c index f5d0dc7d6b..378f67b6ad 100644 --- a/libavfilter/vf_drawtext.c +++ b/libavfilter/vf_drawtext.c @@ -1395,7 +1395,7 @@ static int measure_text(AVFilterContext *ctx, TextMetrics *metrics) { DrawTextContext *s = ctx->priv; char *text = s->expanded_text.str; - char *textdup = NULL, *start = NULL; + char *textdup = NULL; int width64 = 0, w64 = 0; int cur_min_y64 = 0, first_max_y64 = -32000; int first_min_x64 = 32000, last_max_x64 = -32000; @@ -1405,7 +1405,7 @@ static int measure_text(AVFilterContext *ctx, TextMetrics *metrics) Glyph *glyph = NULL; int i, tab_idx = 0, last_tab_idx = 0, line_offset = 0; - char* p; + uint8_t *start, *p; int ret = 0; // Count the lines and the tab characters -- 2.49.1 >From cbf85fea4466fe33516c602c28e5b50da1759096 Mon Sep 17 00:00:00 2001 From: Zhao Zhili <[email protected]> Date: Fri, 14 Nov 2025 17:23:22 +0800 Subject: [PATCH 3/3] avutil/common: make it clear that GET_UTF8/GET_UTF16 need unsigned type 1. Document the type of GET_BYTE/GET_16BIT. 2. Cast to the required type in case of GET_BYTE/GET_16BIT returned incorrect type. --- libavutil/common.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/libavutil/common.h b/libavutil/common.h index 3b830daf30..116a010865 100644 --- a/libavutil/common.h +++ b/libavutil/common.h @@ -471,7 +471,8 @@ static av_always_inline av_const int av_parity_c(uint32_t v) * Convert a UTF-8 character (up to 4 bytes) to its 32-bit UCS-4 encoded form. * * @param val Output value, must be an lvalue of type uint32_t. - * @param GET_BYTE Expression reading one byte from the input. + * @param GET_BYTE Expression reading one byte from the input, must be unsigned + * type like uint8_t. * Evaluated up to 7 times (4 for the currently * assigned Unicode range). With a memory buffer * input, this could be *ptr++, or if you want to make sure @@ -486,13 +487,13 @@ static av_always_inline av_const int av_parity_c(uint32_t v) * to prevent undefined results. */ #define GET_UTF8(val, GET_BYTE, ERROR)\ - val= (GET_BYTE);\ + val= (uint8_t)(GET_BYTE);\ {\ uint32_t top = (val & 128) >> 1;\ if ((val & 0xc0) == 0x80 || val >= 0xFE)\ {ERROR}\ while (val & top) {\ - unsigned int tmp = (GET_BYTE) - 128;\ + unsigned int tmp = (uint8_t)(GET_BYTE) - 128;\ if(tmp>>6)\ {ERROR}\ val= (val<<6) + tmp;\ @@ -506,16 +507,17 @@ static av_always_inline av_const int av_parity_c(uint32_t v) * * @param val Output value, must be an lvalue of type uint32_t. * @param GET_16BIT Expression returning two bytes of UTF-16 data converted - * to native byte order. Evaluated one or two times. + * to native byte order, must be unsigned type like uint16_t. + * Evaluated one or two times. * @param ERROR Expression to be evaluated on invalid input, * typically a goto statement. */ #define GET_UTF16(val, GET_16BIT, ERROR)\ - val = (GET_16BIT);\ + val = (uint16_t)(GET_16BIT);\ {\ unsigned int hi = val - 0xD800;\ if (hi < 0x800) {\ - val = (GET_16BIT) - 0xDC00;\ + val = (uint16_t)(GET_16BIT) - 0xDC00;\ if (val > 0x3FFU || hi > 0x3FFU)\ {ERROR}\ val += (hi<<10) + 0x10000;\ -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
