diff mbox series

[FFmpeg-devel,16/33] fftools/ffmpeg_mux_init: improve of_add_programs()

Message ID 20230713105553.21052-16-anton@khirnov.net
State New
Headers show
Series [FFmpeg-devel,01/33] fftools/ffmpeg_mux_init: return errors from of_open() instead of aborting | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Anton Khirnov July 13, 2023, 10:55 a.m. UTC
Replace duplicated(!) and broken* custom string parsing with
av_dict_parse_string(). Return error codes instead of aborting.

* e.g. it treats NULL returned from av_get_token() as "separator not
  found", when in fact av_get_token() only returns NULL on memory
  allocation failure
---
 fftools/ffmpeg_mux_init.c | 111 ++++++++++++++++++--------------------
 1 file changed, 52 insertions(+), 59 deletions(-)

Comments

Michael Niedermayer July 13, 2023, 11:30 p.m. UTC | #1
On Thu, Jul 13, 2023 at 12:55:36PM +0200, Anton Khirnov wrote:
> Replace duplicated(!) and broken* custom string parsing with
> av_dict_parse_string(). Return error codes instead of aborting.
> 
> * e.g. it treats NULL returned from av_get_token() as "separator not
>   found", when in fact av_get_token() only returns NULL on memory
>   allocation failure
> ---
>  fftools/ffmpeg_mux_init.c | 111 ++++++++++++++++++--------------------
>  1 file changed, 52 insertions(+), 59 deletions(-)

smells like memory corruption

-i ~/videos/01c56b0dc1.ts -copy_unknown -map 0 -c copy -fflags +bitexact  -t 3 -y file-copy-unknown.ts

http://samples.mplayerhq.hu/ts/01c56b0dc1.ts

[mpegts @ 0x2ced6980] Could not find codec parameters for stream 6 (Unknown: none ([5][0][0][0] / 0x0005)): unknown codec
Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options
Input #0, mpegts, from 'videos/01c56b0dc1.ts':
  Duration: 00:00:10.73, start: 40848.136244, bitrate: 8431 kb/s
  Program 1201
  Stream #0:0[0xfb]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(tv, bt709, top first), 1920x1080 [SAR 1:1 DAR 16:9], 25 fps, 50 tbr, 90k tbn
  Stream #0:1[0x12d](eng): Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
  Stream #0:2[0x132](eng): Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp (visual impaired) (descriptions)
  Stream #0:3[0x192](eng): Audio: ac3 ([6][0][0][0] / 0x0006), 48000 Hz, 5.1(side), fltp, 384 kb/s
  Stream #0:4[0x3ea]: Unknown: none ([11][0][0][0] / 0x000B)
  Stream #0:5[0x401](eng): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006) (hearing impaired)
  Stream #0:6[0x1f40]: Unknown: none ([5][0][0][0] / 0x0005)
==2131== Conditional jump or move depends on uninitialised value(s)
==2131==    at 0x3162AF: of_open (in ffmpeg-git/ffmpeg/ffmpeg_g)
==2131==    by 0x31863B: open_files.isra.2 (in ffmpeg-git/ffmpeg/ffmpeg_g)
==2131==    by 0x319BAA: ffmpeg_parse_options (in ffmpeg-git/ffmpeg/ffmpeg_g)
==2131==    by 0x2F7A00: main (in ffmpeg-git/ffmpeg/ffmpeg_g)
==2131==  Uninitialised value was created by a stack allocation
==2131==    at 0x3139DD: of_open (in ffmpeg-git/ffmpeg/ffmpeg_g)
==2131==
==2131== Invalid free() / delete / delete[] / realloc()
==2131==    at 0x4C32D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2131==    by 0x315AEF: of_open (in ffmpeg-git/ffmpeg/ffmpeg_g)
==2131==    by 0x31863B: open_files.isra.2 (in ffmpeg-git/ffmpeg/ffmpeg_g)
==2131==    by 0x319BAA: ffmpeg_parse_options (in ffmpeg-git/ffmpeg/ffmpeg_g)
==2131==    by 0x2F7A00: main (in ffmpeg-git/ffmpeg/ffmpeg_g)
==2131==  Address 0x22f462840 is not stack'd, malloc'd or (recently) free'd
==2131==
Output #0, mpegts, to 'file-copy-unknown.ts':
  Stream #0:0: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(tv, bt709, top first), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 25 fps, 50 tbr, 90k tbn
  Stream #0:1(eng): Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp (default)
  Stream #0:2(eng): Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp (visual impaired) (descriptions)
  Stream #0:3(eng): Audio: ac3 ([6][0][0][0] / 0x0006), 48000 Hz, 5.1(side), fltp, 384 kb/s
  Stream #0:4: Unknown: none ([11][0][0][0] / 0x000B)
  Stream #0:5(eng): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006) (hearing impaired)
  Stream #0:6: Unknown: none ([5][0][0][0] / 0x0005)
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
  Stream #0:1 -> #0:1 (copy)
  Stream #0:2 -> #0:2 (copy)
  Stream #0:3 -> #0:3 (copy)
  Stream #0:4 -> #0:4 (copy)
  Stream #0:5 -> #0:5 (copy)
  Stream #0:6 -> #0:6 (copy)
Press [q] to stop, [?] for help
[mpegts @ 0x2ced6980] PES packet size mismatche=00:00:02.98 bitrate=   0.0kbits/s speed=5.97x
[mpegts @ 0x2ced6980] Packet corrupt (stream = 1, dts = 3677260813).
[in#0/mpegts @ 0x2ced67c0] corrupt input packet in stream 1
[mpegts @ 0x2ced6980] PES packet size mismatch
[mpegts @ 0x2ced6980] Packet corrupt (stream = 2, dts = 3677250303).
[in#0/mpegts @ 0x2ced67c0] corrupt input packet in stream 2
    Last message repeated 3 times
[mpegts @ 0x2ced6980] PES packet size mismatch
[mpegts @ 0x2ced6980] Packet corrupt (stream = 3, dts = 3677239462).
[in#0/mpegts @ 0x2ced67c0] corrupt input packet in stream 3
    Last message repeated 1 times
[mpegts @ 0x2ea0edc0] Stream 4, codec none, is muxed as a private data stream and may not be recognized upon reading.
[mpegts @ 0x2ea0edc0] Stream 6, codec none, is muxed as a private data stream and may not be recognized upon reading.
[out#0/mpegts @ 0x2d09f040] video:484kB audio:206kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 6.355365%
frame=   42 fps=0.0 q=-1.0 Lsize=     734kB time=00:00:02.98 bitrate=2011.2kbits/s speed=3.31x
==2131==
==2131== HEAP SUMMARY:
==2131==     in use at exit: 49,276 bytes in 240 blocks
==2131==   total heap usage: 23,803 allocs, 23,564 frees, 66,233,951 bytes allocated



[...]
Anton Khirnov July 14, 2023, 9:07 a.m. UTC | #2
Quoting Michael Niedermayer (2023-07-14 01:30:24)
> On Thu, Jul 13, 2023 at 12:55:36PM +0200, Anton Khirnov wrote:
> > Replace duplicated(!) and broken* custom string parsing with
> > av_dict_parse_string(). Return error codes instead of aborting.
> > 
> > * e.g. it treats NULL returned from av_get_token() as "separator not
> >   found", when in fact av_get_token() only returns NULL on memory
> >   allocation failure
> > ---
> >  fftools/ffmpeg_mux_init.c | 111 ++++++++++++++++++--------------------
> >  1 file changed, 52 insertions(+), 59 deletions(-)
> 
> smells like memory corruption
> 
> -i ~/videos/01c56b0dc1.ts -copy_unknown -map 0 -c copy -fflags +bitexact  -t 3 -y file-copy-unknown.ts

I have no idea why are you replying to this specific patch, when this
issue apparently exists in current master.

I'll send a fix for it in a later patchset, but it's completely
unrelated to this set.
Michael Niedermayer July 14, 2023, 6:12 p.m. UTC | #3
On Fri, Jul 14, 2023 at 11:07:19AM +0200, Anton Khirnov wrote:
> Quoting Michael Niedermayer (2023-07-14 01:30:24)
> > On Thu, Jul 13, 2023 at 12:55:36PM +0200, Anton Khirnov wrote:
> > > Replace duplicated(!) and broken* custom string parsing with
> > > av_dict_parse_string(). Return error codes instead of aborting.
> > > 
> > > * e.g. it treats NULL returned from av_get_token() as "separator not
> > >   found", when in fact av_get_token() only returns NULL on memory
> > >   allocation failure
> > > ---
> > >  fftools/ffmpeg_mux_init.c | 111 ++++++++++++++++++--------------------
> > >  1 file changed, 52 insertions(+), 59 deletions(-)
> > 
> > smells like memory corruption
> > 
> > -i ~/videos/01c56b0dc1.ts -copy_unknown -map 0 -c copy -fflags +bitexact  -t 3 -y file-copy-unknown.ts
> 
> I have no idea why are you replying to this specific patch, when this
> issue apparently exists in current master.

git bisect of a segfault lead me to this one

with memory corruption, its quite possible it doesnt always segfault so
git bisect results can be bogus

sorry

[...]
diff mbox series

Patch

diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index 3a327048bb..b0befb8924 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -1782,76 +1782,66 @@  static int setup_sync_queues(Muxer *mux, AVFormatContext *oc, int64_t buf_size_u
     return 0;
 }
 
-static void of_add_programs(Muxer *mux, const OptionsContext *o)
+static int of_add_programs(Muxer *mux, const OptionsContext *o)
 {
     AVFormatContext *oc = mux->fc;
     /* process manually set programs */
     for (int i = 0; i < o->nb_program; i++) {
-        const char *p = o->program[i].u.str;
-        int progid = i+1;
+        AVDictionary *dict = NULL;
+        const AVDictionaryEntry *e;
         AVProgram *program;
+        int ret, progid = i + 1;
 
-        while(*p) {
-            const char *p2 = av_get_token(&p, ":");
-            const char *to_dealloc = p2;
-            char *key;
-            if (!p2)
-                break;
+        ret = av_dict_parse_string(&dict, o->program[i].u.str, "=", ":",
+                                   AV_DICT_MULTIKEY);
+        if (ret < 0) {
+            av_log(mux, AV_LOG_ERROR, "Error parsing program specification %s\n",
+                   o->program[i].u.str);
+            return ret;
+        }
 
-            if(*p) p++;
-
-            key = av_get_token(&p2, "=");
-            if (!key || !*p2) {
-                av_freep(&to_dealloc);
-                av_freep(&key);
-                break;
-            }
-            p2++;
-
-            if (!strcmp(key, "program_num"))
-                progid = strtol(p2, NULL, 0);
-            av_freep(&to_dealloc);
-            av_freep(&key);
+        e = av_dict_get(dict, "program_num", NULL, 0);
+        if (e) {
+            progid = strtol(e->value, NULL, 0);
+            av_dict_set(&dict, e->key, NULL, 0);
         }
 
         program = av_new_program(oc, progid);
-        if (!program)
-            report_and_exit(AVERROR(ENOMEM));
-
-        p = o->program[i].u.str;
-        while(*p) {
-            const char *p2 = av_get_token(&p, ":");
-            const char *to_dealloc = p2;
-            char *key;
-            if (!p2)
-                break;
-            if(*p) p++;
-
-            key = av_get_token(&p2, "=");
-            if (!key) {
-                av_log(mux, AV_LOG_FATAL,
-                       "No '=' character in program string %s.\n",
-                       p2);
-                exit_program(1);
-            }
-            if (!*p2)
-                exit_program(1);
-            p2++;
-
-            if (!strcmp(key, "title")) {
-                av_dict_set(&program->metadata, "title", p2, 0);
-            } else if (!strcmp(key, "program_num")) {
-            } else if (!strcmp(key, "st")) {
-                int st_num = strtol(p2, NULL, 0);
-                av_program_add_stream_index(oc, progid, st_num);
-            } else {
-                av_log(mux, AV_LOG_FATAL, "Unknown program key %s.\n", key);
-                exit_program(1);
-            }
-            av_freep(&to_dealloc);
-            av_freep(&key);
+        if (!program) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
         }
+
+        e = av_dict_get(dict, "title", NULL, 0);
+        if (e) {
+            av_dict_set(&program->metadata, e->key, e->value, 0);
+            av_dict_set(&dict, e->key, NULL, 0);
+        }
+
+        e = NULL;
+        while (e = av_dict_get(dict, "st", e, 0)) {
+            int st_num = strtol(e->value, NULL, 0);
+            av_program_add_stream_index(oc, progid, st_num);
+        }
+
+        // make sure that nothing but "st" entries are left in the dict
+        e = NULL;
+        while (e = av_dict_iterate(dict, e)) {
+            if (!strcmp(e->key, "st"))
+                continue;
+
+            av_log(mux, AV_LOG_FATAL, "Unknown program key %s.\n", e->key);
+            ret = AVERROR(EINVAL);
+            goto fail;
+        }
+
+fail:
+        av_dict_free(&dict);
+        if (ret < 0)
+            return ret;
     }
+
+    return 0;
 }
 
 /**
@@ -2547,7 +2537,10 @@  int of_open(const OptionsContext *o, const char *filename)
     if (err < 0)
         return err;
 
-    of_add_programs(mux, o);
+    err = of_add_programs(mux, o);
+    if (err < 0)
+        return err;
+
     of_add_metadata(of, oc, o);
 
     err = set_dispositions(mux, o);