Bug 88716 - [patch] textproc/libextractor fix double free() bug
Summary: [patch] textproc/libextractor fix double free() bug
Status: Closed FIXED
Alias: None
Product: Ports & Packages
Classification: Unclassified
Component: Individual Port(s) (show other bugs)
Version: Latest
Hardware: Any Any
: Normal Affects Only Me
Assignee: Kevin Lo
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-11-09 11:50 UTC by Vasil Dimov
Modified: 2005-11-11 05:11 UTC (History)
1 user (show)

See Also:


Attachments
libextractor_free.diff (1.07 KB, patch)
2005-11-09 11:50 UTC, Vasil Dimov
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Vasil Dimov 2005-11-09 11:50:13 UTC
Problem: `make check' for textproc/libextractor fails:
...
make  check-TESTS
lt-trivialtest in free(): error: chunk is already free
Abort trap (core dumped)
FAIL: trivialtest
PASS: keywordlisttest
lt-plugintest in free(): error: chunk is already free
Abort trap (core dumped)
FAIL: plugintest
lt-multiload in free(): error: chunk is already free
Abort trap (core dumped)
FAIL: multiload
=========================================
3 of 4 tests failed
Please report to bug-libextractor@gnu.org
=========================================
...

(NOTE: options AJ are set via malloc.conf(3))

Lets look closer to one of the failing progs:

$ cd libextractor-0.5.6a/src/test
$ ./.libs/lt-trivialtest
lt-trivialtest in free(): error: chunk is already free
Abort trap: 6 (core dumped)
$

gdb is useless here, because it segfaults itself, so I used printfs to
locate the problem.

the following is self-explanatory trace of the program, created by
inserting printfs in the appropriate places:
(the prog is being run via electricfence to make it crash as soon as
possible)

$ ./.libs/lt-trivialtest
extractor.c:216 le_ltdl_init begin
extractor.c:253 le_ltdl_init end
trivialtest.c:24 main begin
extractor.c:216 le_ltdl_init begin
extractor.c:235 le_ltdl_init old_dlsearchpath=0x801122fcc
extractor.c:237 le_ltdl_init old_dlsearchpath=/usr/lib/libextractor:/usr/local/lib/libextractor
extractor.c:253 le_ltdl_init end
extractor.c:258 le_ltdl_fini begin
ltdl.c:4058 lt_dlsetsearchpath search_path=0x801122fcc
ltdl.c:4060 lt_dlsetsearchpath search_path=/usr/lib/libextractor:/usr/local/lib/libextractor
extractor.c:274 le_ltdl_fini end
trivialtest.c:28 main between
extractor.c:216 le_ltdl_init begin
extractor.c:235 le_ltdl_init old_dlsearchpath=0x802cd3fcc
extractor.c:237 le_ltdl_init old_dlsearchpath=/usr/lib/libextractor:/usr/local/lib/libextractor
extractor.c:253 le_ltdl_init end
extractor.c:258 le_ltdl_fini begin
ltdl.c:4058 lt_dlsetsearchpath search_path=0x802cd3fcc
ltdl.c:4060 lt_dlsetsearchpath search_path=/usr/lib/libextractor:/usr/local/lib/libextractor
extractor.c:274 le_ltdl_fini end
trivialtest.c:31 main end
extractor.c:258 le_ltdl_fini begin
ltdl.c:4058 lt_dlsetsearchpath search_path=0x802cd3fcc
Bus error: 10 (core dumped)
$

You see, the destructor le_ltdl_fini() is being called "unexpectedly" when
the program exits, but search_path=0x802cd3fcc has already been free()d by
the previous invocation.

Fix: it seems quite trivial, we just need to old_dlsearchpath = NULL
after free()ing it - lt_dlsetsearchpath() is checking if called with NULL
pointer and we are calling free() only if old_dlsearchpath is non-NULL.

With the included patch, output of trivialtest looks like:

$ ./.libs/lt-trivialtest
extractor.c:216 le_ltdl_init begin
extractor.c:253 le_ltdl_init end
trivialtest.c:24 main begin
extractor.c:216 le_ltdl_init begin
extractor.c:235 le_ltdl_init old_dlsearchpath=0x801122fcc
extractor.c:237 le_ltdl_init old_dlsearchpath=/usr/lib/libextractor:/usr/local/lib/libextractor
extractor.c:253 le_ltdl_init end
extractor.c:258 le_ltdl_fini begin
ltdl.c:4058 lt_dlsetsearchpath search_path=0x801122fcc
ltdl.c:4060 lt_dlsetsearchpath search_path=/usr/lib/libextractor:/usr/local/lib/libextractor
extractor.c:274 le_ltdl_fini end
trivialtest.c:28 main between
extractor.c:216 le_ltdl_init begin
extractor.c:235 le_ltdl_init old_dlsearchpath=0x802cd3fcc
extractor.c:237 le_ltdl_init old_dlsearchpath=/usr/lib/libextractor:/usr/local/lib/libextractor
extractor.c:253 le_ltdl_init end
extractor.c:258 le_ltdl_fini begin
ltdl.c:4058 lt_dlsetsearchpath search_path=0x802cd3fcc
ltdl.c:4060 lt_dlsetsearchpath search_path=/usr/lib/libextractor:/usr/local/lib/libextractor
extractor.c:274 le_ltdl_fini end
trivialtest.c:31 main end
extractor.c:258 le_ltdl_fini begin
ltdl.c:4058 lt_dlsetsearchpath search_path=0x0
ltdl.c:4060 lt_dlsetsearchpath search_path=(null)
extractor.c:274 le_ltdl_fini end
$

And of-course...
$ make check
...
==================
All 4 tests passed
==================
$

How-To-Repeat: 
export MALLOC_OPTIONS=AJ
cd /usr/ports/textproc/libextractor
make check
Comment 1 Edwin Groothuis freebsd_committer freebsd_triage 2005-11-09 11:51:14 UTC
Responsible Changed
From-To: freebsd-ports-bugs->kevlo

Over to maintainer 

http://www.freebsd.org/cgi/query-pr.cgi?pr=88716 

Adding to audit trail from misfiled PR ports/88748:

Date: Wed, 9 Nov 2005 12:53:05 -0800
Comment 2 Vasil Dimov 2005-11-10 09:23:54 UTC
On Wed, Nov 09, 2005 at 12:53:05PM -0800, Christian Grothoff wrote:
> Patch looks good and will be in the next LE release (is already in subversion 
> 2237).  I'm still not sure under which circumstances the _destructor_ 
> function is invoked twice without _constructor_ in the meantime (BSD-ism?) 


The constructor and the destructor are called 3 times each:

$ ./.libs/lt-trivialtest
// constructor call 1, does not initialize old_dlsearchpath
extractor.c:215 le_ltdl_init begin old_dlsearchpath=0x0
extractor.c:246 le_ltdl_init end old_dlsearchpath=0x0

trivialtest.c:23 main begin

// constructor call 2, initializes old_dlsearchpath
extractor.c:215 le_ltdl_init begin old_dlsearchpath=0x0
extractor.c:246 le_ltdl_init end old_dlsearchpath=0x501280

// destructor call 1, frees old_dlsearchpath
extractor.c:250 le_ltdl_fini begin old_dlsearchpath=0x501280
extractor.c:253 le_ltdl_fini free(0x501280)
extractor.c:262 le_ltdl_fini end old_dlsearchpath=0x501280

// constructor call 3, initializes old_dlsearchpath (again)
extractor.c:215 le_ltdl_init begin old_dlsearchpath=0x501280
extractor.c:246 le_ltdl_init end old_dlsearchpath=0x5012c0

// destructor call 2, frees old_dlsearchpath,
// initialized from constructor call 3
extractor.c:250 le_ltdl_fini begin old_dlsearchpath=0x5012c0
extractor.c:253 le_ltdl_fini free(0x5012c0)
extractor.c:262 le_ltdl_fini end old_dlsearchpath=0x5012c0

trivialtest.c:27 main end

// destructor call 3, attempts to free old_dlsearchpath, initialized
// from constructor call 3
extractor.c:250 le_ltdl_fini begin old_dlsearchpath=0x5012c0
extractor.c:253 le_ltdl_fini free(0x5012c0)
lt-trivialtest in free(): error: chunk is already free
Abort trap: 6 (core dumped)
$

Lets see what happens on Linux:

$ uname -a
Linux fqdn 2.4.18-14 #1 Wed Sep 4 13:35:50 EDT 2002 i686 i686 i386 GNU/Linux

$ /lib/libc.so.6
GNU C Library development release version 2.2.93, by Roland McGrath et al.
Copyright (C) 1992-2001, 2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 3.2 20020903 (Red Hat Linux 8.0 3.2-7).
Compiled on a Linux 2.4.9-9 system on 2002-09-05.
Available extensions:
        GNU libio by Per Bothner
        crypt add-on version 2.1 by Michael Glad and others
        The C stubs add-on version 2.1.2.
        linuxthreads-0.10 by Xavier Leroy
        BIND-8.2.3-T5B
        NIS(YP)/NIS+ NSS modules 0.19 by Thorsten Kukuk
        Glibc-2.0 compatibility add-on by Cristian Gafton
        libthread_db work sponsored by Alpha Processor Inc
Report bugs using the `glibcbug' script to <bugs@gnu.org>.

$ gcc --version
gcc (GCC) 3.2 20020903 (Red Hat Linux 8.0 3.2-7)
...

$ ./.libs/lt-trivialtest
extractor.c:215 le_ltdl_init begin old_dlsearchpath=(nil)
extractor.c:246 le_ltdl_init end old_dlsearchpath=(nil)
trivialtest.c:23 main begin
trivialtest.c:27 main end
extractor.c:250 le_ltdl_fini begin old_dlsearchpath=(nil)
extractor.c:262 le_ltdl_fini end old_dlsearchpath=(nil)
$

Hmmz, looks quite different.

-- 
Vasil Dimov
Comment 3 Kevin Lo freebsd_committer freebsd_triage 2005-11-11 05:10:45 UTC
State Changed
From-To: open->closed

Committed, thanks.