Sun Oct 22 17:55:43 2006

Ticket #2 (assigned)

ogg.vorbis.VorbisFile() destructor kills python (redundant fclose() -> double free())


Priority: normal Reporter: Greg Ward <gward-pyogg@python.net>
Severity: blocker Assigned to: soyt (accepted)
Component: legacy-vorbis Status: assigned
Version: 1.0 Resolution:  
Milestone:   Keywords:  

Description by Greg Ward <gward-pyogg@python.net>:

Using pyogg-1.3 and pyvorbis-1.4 causes Python to die horribly:

$ python
Python 2.4.4c1 (#2, Oct 12 2006, 21:41:47)
[GCC 4.1.2 20061007 (prerelease) (Debian 4.1.1-16)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import ogg.vorbis
>>> ogg.vorbis.VorbisFile("/music/ogg/metallica/master_of_puppets/01.battery.ogg")
<VorbisFile object at 0xb7db4548>
>>> ^D
*** glibc detected *** double free or corruption (!prev): 0x081b3a80 ***
zsh: abort      python

This is on a Debian 'unstable' system running Linux 2.6.18 and glibc 2.3.6. I get the same result with Debian's current python2.3 package (which is 2.3.5).

Needless to say, this makes pyvorbis 1.4 unusable.

Attachments

  • pyogg-ticket2-test.patch (1 kB) - Patch: add test_legacy.py to demonstrate the bug with current svn code, added by Greg Ward <gerg.ward+pyogg@gmail.com> on Sun Oct 22 18:14:43 2006.
  • pyogg-ticket2-fix.patch (767 bytes) - fix for this bug (and 3 other memory-related problems), added by Greg Ward <gerg.ward+pyogg@gmail.com> on Sun Oct 22 20:09:48 2006.

Changelog

Sun Oct 22 18:14:43 2006: Modified by Greg Ward <gerg.ward+pyogg@gmail.com>

  • attachment added: pyogg-ticket2-test.patch

Sun Oct 22 18:16:01 2006: Modified by Greg Ward <gerg.ward+pyogg@gmail.com>

    Bug is present in latest svn code: I just added a patch to verify this, by adding ogg/test/test_legacy.py.

    Sun Oct 22 20:08:40 2006: Modified by Greg Ward <gerg.ward+pyogg@gmail.com>

      OK, I figured it out. The problem is that `py_ov_file_dealloc()` calls `fclose(py_self->c_file)`, but the libvorbis docs say not to:

      Also, you should be aware that ov_open(), once successful, takes complete possession of the file resource. After you have opened a file using ov_open(), you MUST close it using ov_clear(), not fclose() or any other function.

      (See http://xiph.org/vorbis/doc/vorbisfile/ov_open.html. This is brain damage on the part of libvorbis, but at least it's documented brain damage.)

      While auditing the code, I discovered what look like three other memory-related problems. While I am no expert at writing Python extensions, I'm positive that one of these (#3) is a real problem (because I could reproduce it), and fairly sure that the other two are as well.

      I'll attach a patch that addresses all four problems:

      1. `py_file_new()` incorrectly calls `Py_DECREF` on the returned object. From http://docs.python.org/ext/ownershipRules.html :

      The object reference returned from a C function that is called from Python must be an owned reference -- ownership is tranferred from the function to its caller.

      2. the redundant `fclose()` call

      3. while `py_ov_file_open()` mallocs an OggVorbis_File struct, it is never freed; I've added a `free()` call in `py_ov_file_dealloc()`. The leak is trivial to demonstrate with this script:

      from ogg._vorbis import VorbisFile
      from ogg.test import common
      
      filename = common.getSampleFilename()
      while True:
          VorbisFile(filename)
      

      4. The `py_vorbisfile` object is created with PyObject_NEW() but destroyed with PyMem_DEL(). This is wrong; you're not supposed to mix and match memory allocation layers like this. The fix is to use PyObject_DEL().

      Sun Oct 22 20:09:48 2006: Modified by Greg Ward <gerg.ward+pyogg@gmail.com>

      • attachment added: pyogg-ticket2-fix.patch

      Sun Oct 22 20:10:34 2006: Modified by Greg Ward <gerg.ward+pyogg@gmail.com>

      • summary changed from ogg.vorbis.VorbisFile() constructor kills python to ogg.vorbis.VorbisFile() destructor kills python (redundant fclose() -> double free())

      Mon Oct 23 08:57:12 2006: Modified by soyt

      • status changed from new to assigned

      Thanks a lot for looking into this. This is very much appreciated. I'll have a look as soon as I have some time, and I'll probably make a new legacy release. The main reason I wanted to go for a rewrite is the amount of C code to maintain in the current code.