dbi icon indicating copy to clipboard operation
dbi copied to clipboard

Memory corruption via selectrow_array + SQLite UDF

Open hoehrmann opened this issue 3 years ago • 0 comments

Some time ago I debugged weird behavior when using DBD::SQLite with sqlite_create_function. Valgrind had this:

==31881== Invalid write of size 8
==31881==    at 0xCAAFD78: XS_DBD__SQLite__db_selectrow_arrayref (in ...perl-5.28.1/lib/site_perl/5.28.1/x86_64-linux/auto/DBD/SQLite/SQLite.so)
==31881==    by 0xC487486: XS_DBI_dispatch (in ...perl-5.28.1/lib/site_perl/5.28.1/x86_64-linux/auto/DBI/DBI.so)
==31881==    by 0x1E73E7: Perl_pp_entersub (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1DDB42: Perl_runops_standard (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1659B3: perl_run (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1401D1: main (in ...perl-5.28.1/bin/perl)
==31881==  Address 0xd5e1ac8 is 376 bytes inside a block of size 11,704 free'd
==31881==    at 0x4C33D2F: realloc (vg_replace_malloc.c:785)
==31881==    by 0x1C16B9: Perl_safesysrealloc (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1DC2B9: Perl_av_extend_guts (in ...perl-5.28.1/bin/perl)
==31881==    by 0x2152FE: Perl_stack_grow (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1DDDCD: S_pushav (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1E1902: Perl_pp_rv2av (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1DDB42: Perl_runops_standard (in ...perl-5.28.1/bin/perl)
==31881==    by 0x15E9F3: Perl_call_sv (in ...perl-5.28.1/bin/perl)
==31881==    by 0xCABC905: sqlite_db_func_dispatcher (in ...perl-5.28.1/lib/site_perl/5.28.1/x86_64-linux/auto/DBD/SQLite/SQLite.so)
==31881==    by 0xCB4C4F7: sqlite3VdbeExec (in ...perl-5.28.1/lib/site_perl/5.28.1/x86_64-linux/auto/DBD/SQLite/SQLite.so)
==31881==    by 0xCB53D6E: sqlite3_step (in ...perl-5.28.1/lib/site_perl/5.28.1/x86_64-linux/auto/DBD/SQLite/SQLite.so)
==31881==    by 0xCAC003E: sqlite_st_execute (in ...perl-5.28.1/lib/site_perl/5.28.1/x86_64-linux/auto/DBD/SQLite/SQLite.so)
==31881==  Block was alloc'd at
==31881==    at 0x4C33D2F: realloc (vg_replace_malloc.c:785)
==31881==    by 0x1C16B9: Perl_safesysrealloc (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1DC2B9: Perl_av_extend_guts (in ...perl-5.28.1/bin/perl)
==31881==    by 0x2152FE: Perl_stack_grow (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1DDDCD: S_pushav (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1E1902: Perl_pp_rv2av (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1DDB42: Perl_runops_standard (in ...perl-5.28.1/bin/perl)
==31881==    by 0x15E831: Perl_call_sv (in ...perl-5.28.1/bin/perl)
==31881==    by 0xC487DCD: XS_DBI_dispatch (in ...perl-5.28.1/lib/site_perl/5.28.1/x86_64-linux/auto/DBI/DBI.so)
==31881==    by 0x1E73E7: Perl_pp_entersub (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1DDB42: Perl_runops_standard (in ...perl-5.28.1/bin/perl)
==31881==    by 0x1659B3: perl_run (in ...perl-5.28.1/bin/perl)

I had short-lived success avoiding this problem by appending

join(' ', (' ') x 100000)

to the return value of my sqlite_create_function function...

I now have a debug build of Perl 5.32.1 with all modules up to date and get

perl: DBI.xs:4124: XS_DBI_dispatch: Assertion `tmpXSoff >= 0' failed.

That means in XS_DBI_dispatch outitems is negative, which could happen through the use_xsbypass logic or by call_sv returning a negative value.

I have tried running this with PERL_DBI_XSBYPASS=0 but would still get the assertion failure.

My DBI call is basically selectrow_array($sql, {}, $string).

With DBI_TRACE=15 the log ends like this (notice the -241095 items):

    >> selectrow_array DISPATCH (DBI::db=HASH(0x55c111269908) rc1/1 @4 g3 ima2001 pid#25674) at ...
    -> selectrow_array for DBD::SQLite::db (DBI::db=HASH(0x55c111269908)~0x55c111309888 '
    SELECT _...
  ' HASH(0x55c1111ac0d0) '...')
    >> prepare     DISPATCH (DBI::db=HASH(0x55c111309888) rc1/1 @3 g2 imaa201 pid#25674) at ...
1   -> prepare for DBD::SQLite::db (DBI::db=HASH(0x55c111309888)~INNER '
    SELECT ...
  ' HASH(0x55c1111ac0d0))
    New 'DBI::st' (for DBD::SQLite::st, parent=DBI::db=HASH(0x55c111309888), id=undef)
    dbih_setup_handle(DBI::st=HASH(0x55c111092770)=>DBI::st=HASH(0x55c11122c758), DBD::SQLite::st, 55c11130b210, Null!)
    dbih_make_com(DBI::db=HASH(0x55c111309888), 55c11119ac40, DBD::SQLite::st, 232, 0) thr#0
    dbih_setup_attrib(DBI::st=HASH(0x55c11122c758), Err, DBI::db=HASH(0x55c111309888)) SCALAR(0x55c110df76a8) (already defined)
    dbih_setup_attrib(DBI::st=HASH(0x55c11122c758), State, DBI::db=HASH(0x55c111309888)) SCALAR(0x55c11133a820) (already defined)
    dbih_setup_attrib(DBI::st=HASH(0x55c11122c758), Errstr, DBI::db=HASH(0x55c111309888)) SCALAR(0x55c110df7f30) (already defined)
    dbih_setup_attrib(DBI::st=HASH(0x55c11122c758), TraceLevel, DBI::db=HASH(0x55c111309888)) 0 (already defined)
    dbih_setup_attrib(DBI::st=HASH(0x55c11122c758), FetchHashKeyName, DBI::db=HASH(0x55c111309888)) 'NAME' (already defined)
    dbih_setup_attrib(DBI::st=HASH(0x55c11122c758), HandleSetErr, DBI::db=HASH(0x55c111309888)) undef (not defined)
    dbih_setup_attrib(DBI::st=HASH(0x55c11122c758), HandleError, DBI::db=HASH(0x55c111309888)) undef (not defined)
    dbih_setup_attrib(DBI::st=HASH(0x55c11122c758), ReadOnly, DBI::db=HASH(0x55c111309888)) undef (not defined)
    dbih_setup_attrib(DBI::st=HASH(0x55c11122c758), Profile, DBI::db=HASH(0x55c111309888)) undef (not defined)
sqlite trace: prepare statement: 
    SELECT ...
1   <- prepare= ( DBI::st=HASH(0x55c111092770) ) [1 items] at ...
sqlite trace: bind into 0x55c1111771a8: 1 => ... (0) pos 0 at dbdimp.c line 1522
sqlite trace: executing 
    SELECT ...
   at dbdimp.c line 980
sqlite trace: bind 0 type 5 as ... at dbdimp.c line 997
sqlite trace: Execute returned 1 cols at dbdimp.c line 1093
sqlite trace: exec ok - 0 rows, 1 cols at dbdimp.c line 1127
sqlite trace: numFields == 1, nrow == 0 at dbdimp.c line 1167
    dbih_setup_fbav alloc for 1 fields
    dbih_setup_fbav now 1 fields
sqlite trace: fetch column 0 as text at dbdimp.c line 1217
    <- selectrow_array= ( ) [-241095 items] at ...
perl: DBI.xs:4124: XS_DBI_dispatch: Assertion `tmpXSoff >= 0' failed.

Calling prepare/execute/fetchrow_array instead works fine.

hoehrmann avatar Apr 04 '21 00:04 hoehrmann