pmqs / DB_File

DB_File - Perl5 access to Berkeley DB version 1.x
8 stars 3 forks source link

DB_File leaks handle on croak #3

Closed iabyn closed 4 years ago

iabyn commented 4 years ago

In bleadperl under Address Sanitizer, 3 of the test files (db-btree, db-hash, db-recno) report leaks. This appears to be because a croak doesn't close the recently-opened DB handle, thus not freeing its resources. The attached proof-of-concept diff makes the issue apparently go away (tested only on DB 5.3.28)

db_file.txt

pmqs commented 4 years ago

Thanks @iabyn,

I thought I'd already run DB_File through Address Sanatizer without finding anything. May have been an older version.

Could you share the Address Sanatizer settings you were using and I'll see if I can reproduce the issue?

iabyn commented 4 years ago

On Fri, Jan 03, 2020 at 05:53:42AM -0800, Paul Marquess wrote:

Thanks @iabyn,

I thought I'd already run DB_File through Address Sanatizer without finding anything. May have been an older version.

Could you share the Address Sanatizer settings you were using and I'll see if I can reproduce the issue?

With my current directory being /home/davem/perl5/git/bleed/, I'm using bleadperl configured as:

config_args='-des -Dusedevel -Dprefix=/home/davem/perl5/git/bleed.out -Uinstallusrbinperl -Duseithreads -Doptimize=-g -Accflags=-DDEBUGGING -ggdb -fsanitize=address -Werror=declaration-after-statement -fno-omit-frame-pointer -fno-common -fsanitize-blacklist=/home/davem/perl5/git/bleed/asan_ignore -Aldflags=-fsanitize=address -Dcc=clang'

Then invoking as:

$ PERL_DESTRUCT_LEVEL=2 ./perl -Ilib t/harness cpan/DB_File/t/*.t

Running one test file gave:

================================================================= ==3659==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 1456 byte(s) in 1 object(s) allocated from:

0 0x50535f in malloc (/home/davem/perl5/git/bleed/perl+0x50535f)

#1 0x7f33292ee1f4 in __os_malloc (/lib64/libdb-5.3.so+0x15c1f4)
#2 0x61d00005467f  (<unknown module>)

Indirect leak of 2088 byte(s) in 1 object(s) allocated from:

0 0x50535f in malloc (/home/davem/perl5/git/bleed/perl+0x50535f)

#1 0x7f33292ee1f4 in __os_malloc (/lib64/libdb-5.3.so+0x15c1f4)
#2 0x620000000c07  (<unknown module>)

Indirect leak of 1656 byte(s) in 7 object(s) allocated from:

0 0x50535f in malloc (/home/davem/perl5/git/bleed/perl+0x50535f)

#1 0x7f33292ee1f4 in __os_malloc (/lib64/libdb-5.3.so+0x15c1f4)
#2 0x744b3ec4448fc3ff  (<unknown module>)

Indirect leak of 32 byte(s) in 1 object(s) allocated from:

0 0x50535f in malloc (/home/davem/perl5/git/bleed/perl+0x50535f)

#1 0x7f33292ee1f4 in __os_malloc (/lib64/libdb-5.3.so+0x15c1f4)

SUMMARY: AddressSanitizer: 5232 byte(s) leaked in 10 allocation(s).

With my patch, all the errors went away.

-- Modern art: "That's easy, I could have done that!" "Ah, but you didn't!"

pmqs commented 4 years ago

Thanks @iabyn

pmqs commented 4 years ago

Issue reproduced.

The leak can be triggered by with this code (taken from t/db-btree.t

use blib;
use DB_File;

# check that attempting to tie an array to a DB_BTREE will fail

my $filename = "xyz" ;
my @x ;
tie @x, 'DB_File', $filename, O_RDWR|O_CREAT, 0640, $DB_BTREE ; 
unlink $filename ;

Get this error

=9990==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 1456 byte(s) in 1 object(s) allocated from:
    #0 0x7f6146141448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x7f614212d774 in __os_malloc (/usr/lib/x86_64-linux-gnu/libdb-5.3.so+0x147774)
    #2 0x61d00005e67f  (<unknown module>)

Indirect leak of 2120 byte(s) in 2 object(s) allocated from:
    #0 0x7f6146141448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x7f614212d774 in __os_malloc (/usr/lib/x86_64-linux-gnu/libdb-5.3.so+0x147774)

Indirect leak of 1656 byte(s) in 7 object(s) allocated from:
    #0 0x7f6146141448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x7f614212d774 in __os_malloc (/usr/lib/x86_64-linux-gnu/libdb-5.3.so+0x147774)
    #2 0x80967fb4538a37ff  (<unknown module>)

The issue is triggered by code in ParseOpenInfo that first calls db_create without any problem. It then checks that the SV that will be tied to is a reference to the correct type. In the sample code above it triggers this croak_and_free.

    if (!isHASH)
        croak_and_free("DB_File can only tie an associative array to a DB_HASH database") ;

In croak_and_free all the malloc'ed memory should be freed. Issue here is a successful call to db_create needs to then call db_close to free memory that Berkeley DB has allocated behind the scenes, In this case that isn't happening.

pmqs commented 4 years ago

Fix included in DB_File 1.853. Closing issue