Opened 4 years ago

Closed 4 years ago

Last modified 3 years ago

#5584 closed defect (needs_more_info)

FFmpeg libraries leak 1MiB memory chunks on corrupt files

Reported by: mocmaint Owned by:
Priority: important Component: undetermined
Version: git-master Keywords: leak regression
Cc: Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description

I am testing the next patchset for the MOC player which adds the ability to feed FFmpeg's libav* libraries input via the callbacks registered using avio_alloc_context(). Some of those tests involve corrupt, empty or otherwise corrupt or invalid files.

When attempting to process a corrupt or invalid file the FFmpeg libraries leak a significant (> 1MiB) block of memory per file.

As this is a library problem, there is no command line invocation of an FFmpeg utility, and those utilities do not appear to use callbacks via avio_alloc_context().

There is no console output (obviously) but the messages issued by the libraries (on one test file) are:

Estimating duration from bitrate, this may be inaccurate
Format flac detected only with low score of 1, misdetection possible!
Could not find codec parameters for stream 0 (Audio: flac, 0 channels): unspecified sample format
Consider increasing the value for the 'analyzeduration' and 'probesize' options

The sequence of library calls (omitting error checking) is:

AVFormatContext *ic = avformat_alloc_context();
ic->pb = avio_alloc_context(...);
avformat_open_input(ic, ...);
avformat_find_stream_info(ic, ...);

avformat_find_stream_info() fails with -541478725.

ValGrind? reports:

==8563==
==8563== HEAP SUMMARY:
==8563== in use at exit: 1,211,448 bytes in 4,717 blocks
==8563== total heap usage: 41,887 allocs, 37,170 frees, 164,363,673 bytes allocated
==8563==
==8563== 1,048,608 bytes in 1 blocks are definitely lost in loss record 279 of 279
==8563== at 0x4C28E62: realloc (in /usr/local/stow/valgrind-3.9.0/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==8563== by 0x98D8940: ???
==8563== by 0xAD7E38C: ???
==8563== by 0xAE4AC24: ???
==8563== by 0x96B8E29: ???
==8563== by 0x96B94D3: ???
==8563== by 0x44D605: play_file (player.c:763)
==8563== by 0x44DCC9: player (player.c:899)
==8563== by 0x420029: play_thread (audio.c:447)
==8563== by 0x568397F: start_thread (in /lib64/libpthread-2.11.1.so)
==8563== by 0xF9D970F: ???
==8563==
==8563== LEAK SUMMARY:
==8563== definitely lost: 1,048,608 bytes in 1 blocks
==8563== indirectly lost: 0 bytes in 0 blocks
==8563== possibly lost: 0 bytes in 0 blocks
==8563== still reachable: 13,926 bytes in 124 blocks
==8563== suppressed: 148,914 bytes in 4,592 blocks
==8563== Reachable blocks (those to which a pointer was found) are not shown.
==8563== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==8563==

A suitable test file can be generated with:

touch bad-flac.flac

Additional notes:

  • This 1MB memory leak was introduced in FFmpeg commit 80a3a66. Therefore it exists from FFmpeg 2.4 onwards and is still present in commit a0349ae2.
  • It was not present in either commit 3a19405 or commit 31e0b5d which commit 80a3a66 merged.
  • It is triggered when using callbacks for I/O and an empty file is opened.
  • Also occurs with files containing random data.
  • It does not occur if callbacks are not used.
  • Size is always 1MiB + 32B: 1,048,608.
  • One such block is leaked for each corrupt or invalid file attempted.
  • The Xiph FLAC libraries handle these files without problems.

Change History (13)

comment:1 Changed 4 years ago by cehoyos

  • Keywords regression added; memory removed
  • Priority changed from normal to important

Please recompile without --disable-debug and provide valgrind output with --leak-check=full and provide source code that allows to reproduce the issue.

comment:2 Changed 4 years ago by mocmaint

FFmpeg was previously built without '--disable-debug'. Full FFmpeg configure originally used is:

CFLAGS="-O2 -fPIC" ./configure --prefix=/usr/local/stow/$(basename $PWD) --libdir='${prefix}/lib64' --shlibdir='${prefix}/lib64' --enable-shared --disable-static --disable-yasm --enable-libspeex --enable-libtheora --enable-libopus --enable-libgme --enable-rpath

'$PWD' was '/tmp/ffmpeg-a0349ae2'.

Adding '--enable-debug' made no difference to ValGrind? output (specificly, backtrace still showed '???').

ValGrind? was previously run with '--leak-check=full --show-leak-kinds=indirect,definite'. Full ValGrind? invocation originally used is:

valgrind --tool=memcheck --leak-check=full --show-leak-kinds=indirect,definite --track-origins=yes --track-fds=yes --gen-suppressions=all --suppressions=/home/jcf/moc/valgrind/moc.supp --suppressions=/home/jcf/moc/valgrind/ffmpeg.supp --suppressions=/home/jcf/moc/valgrind/libasound.supp --suppressions=/home/jcf/moc/valgrind/libgme.supp --suppressions=/home/jcf/moc/valgrind/libguile.supp --suppressions=/home/jcf/moc/valgrind/libmagic.supp --suppressions=/home/jcf/moc/valgrind/librcc.supp --suppressions=/home/jcf/moc/valgrind/libspeex.supp --suppressions=/home/jcf/moc/valgrind/libdb.supp --suppressions=/home/jcf/moc/valgrind/vfprintf.supp --log-file=valgrind.8563 ../moc-test/mocp -D -J -T /home/jcf/moc/testing/themes/transparent-background .

Changing to '--show-leak-kinds=all' made no difference to ValGrind? output. ValGrind? version being used is 3.9.0.

Trying to build MOC against an FFmpeg built with static (rather than shared libraries) failed with:

/usr/lib64/gcc/x86_64-slackware-linux/4.4.4/../../../../x86_64-slackware-linux/bin/ld: /usr/local/stow/ffmpeg-a0349ae2/lib64/libavcodec.a(cavsdsp.o): relocation R_X86_64_PC32 against symbol `ff_pw_5' can not be used when making a shared object; recompile with -fPIC
/usr/lib64/gcc/x86_64-slackware-linux/4.4.4/../../../../x86_64-slackware-linux/bin/ld: final link failed: Bad value

MOC is available using:

svn co -r2872 svn://svn.daper.net/moc/trunk

Aggregated patch for the patchset being tested is available using:

wget ftp://ftp.daper.net/pub/soft/moc/unstable/moc-r2872+ffmpeg_memleak-1.patch.gz

Apply to the SVN trunk revision 2872 using:

zcat moc-r2872+ffmpeg_memleak-1.patch.gz | patch -p1

The MOC FFmpeg decoder will be found in:

decoder_plugins/ffmpeg

Build as per instructions in README. After installing, delete all installed decoder plugins in 'lib{,64}/moc/decoder_plugins' *except* 'libffmpeg_decoder.*'.

Then launch MOC as 'mocp' in the directory containing corrupt files.

Last edited 4 years ago by mocmaint (previous) (diff)

comment:3 Changed 4 years ago by michael

--2016-06-07 14:16:34--  ftp://ftp.daper.net/pub/soft/moc/unstable/moc-r2872+ffmpeg_memleak-1.patch.gz
           => `moc-r2872+ffmpeg_memleak-1.patch.gz'
Resolving ftp.daper.net (ftp.daper.net)... 188.165.19.194
Connecting to ftp.daper.net (ftp.daper.net)|188.165.19.194|:21... connected.
Logging in as anonymous ... Logged in!
==> SYST ... done.    ==> PWD ... done.
==> TYPE I ... done.  ==> CWD (1) /pub/soft/moc/unstable ... done.
==> SIZE moc-r2872+ffmpeg_memleak-1.patch.gz ... 10930
==> PASV ... done.    ==> RETR moc-r2872+ffmpeg_memleak-1.patch.gz ... 
No such file `moc-r2872+ffmpeg_memleak-1.patch.gz'.

comment:4 Changed 4 years ago by mocmaint

Sorry, I forgot to make it world-readable. (That always trips me up.) Try again now.

comment:5 Changed 4 years ago by michael

is this unrelated to the non zero at EOF value ?

comment:6 Changed 4 years ago by mocmaint

Yes, it is unrelated. Wild seeking occurs in W64 only; 1MiB leak occurs for all tested formats. I did retest with the wild seeking circumvention and it made no difference to this problem.

comment:7 Changed 4 years ago by mocmaint

I have created a test rig for you which is available at:

ftp://ftp.daper.net/pub/soft/moc/unstable/leak-test.c.gz

This test rig emulates the essential behaviour of MOC and triggers the problem on empty and corrupt files (but not on others).

Please read the comments at the start of the file on compiling and running.

In creating this test rig, FFmpeg's behaviour became clearer. Even though the read callback returns end of file (and assuming that zero represents that condition), FFmpeg continues making successive calls doubling the size of the buffer each time until the ~1MiB size is reached, after which it returns an error from avformat_open_input(). It is probably this buffer which is being leaked, even if avformat_close_input() and/or av_freep()s are called. When clean up is attempted (by calling those functions) a segfault is sometimes triggered.

ValGrind? reports an invalid free in all cases, so FFmpeg is probably freeing the wrong block of storage.

All tests above used FFmpeg at e8a236ad.

comment:8 Changed 4 years ago by cehoyos

Could you use your test rig to provide useful valgrind output using a library with debug information?

comment:9 Changed 4 years ago by mocmaint

Yes you could, but I'm not sure it'd gain you much. By now I'd expect that you're looking for a loop which is doubling a buffer size and ignoring the return value of the read callback then failing to free the buffer. I'm sure you know your codebase well enough to have a fairly good idea of where to look.

comment:10 Changed 4 years ago by heleppkes

For one thing, if you allocate your own custom AVIO context, you also need to free it with avio_close(p), avformat_close_input is not going to do it for you (since you allocated it, and thus manage it).

Otherwise, unless you can provide proper valgrind output with backtraces where these buffers are allocated, you really should not expect us to go digging for any possible fault somewhere. If it would be a common problem, it would've shown up before.

comment:11 Changed 4 years ago by mocmaint

  • Resolution set to wontfix
  • Status changed from new to closed

For one thing, if you allocate your own custom AVIO context, you also need to free it with avio_close(p), avformat_close_input is not going to do it for you (since you allocated it, and thus manage it).

I obviously misinterpreted the avio_close() comment:

"This function can only be used if s was opened by avio_open()."

as meaning that avio_close() can only be used if s was opened by avio_open().

Otherwise, unless you can provide proper valgrind output with backtraces where these buffers are allocated, you really should not expect us to go digging for any possible fault somewhere.

Oh, okay then. I'm sorry I didn't provide enough information above.

If it would be a common problem, it would've shown up before.

Yeah, I do have a reputation for finding bugs no one else has spotted.

comment:12 Changed 4 years ago by cehoyos

  • Resolution changed from wontfix to needs_more_info

comment:13 Changed 3 years ago by mocmaint

I have now commited the latest MOC patchset which includes a circumvention for this FFmpeg bug and it no longer impacts upon MOC. I will leave it in your hands to pursue it or close it as you wish.

Note: See TracTickets for help on using tickets.