Bug #26388 server side prepare issues
Submitted: 15 Feb 2007 1:50 Modified: 13 Aug 2007 16:09
Reporter: [ name withheld ] Email Updates:
Status: No Feedback Impact on me:
None 
Category:Connectors: DBD::mysql ( Perl ) Severity:S2 (Serious)
Version:4.001, 4.003 OS:Solaris (Solaris)
Assigned to: CPU Architecture:Any
Tags: Contribution, core dump, server side prepare, SIGSEGV

[15 Feb 2007 1:50] [ name withheld ]
Description:
* imp_sth->bind and imp_sth->fbind should be freed in dbd_st_destroy()
  rather than in dbd_st_finish(); they are required for reusing statement handles,
  otherwise result in SIGSEGV.

* Using a field type as a bind type is incorrect;
      SELECT num_col FROM t WHERE str_col = ?
  in this case, field[0] would be one of the numeric types, but the bind type should be a string.

* Type correction for bind types.

* supress warnings produced by gcc with -Wall; unused variables, type mismatches, etc.

and so on.

How to repeat:
my $dbh = DBI->connect('DBI:mysql:dbname;mysql_server_prepare=1', 'user', 'passwd') or die;
my $sth = $dbh->prepare('INSERT t (a, b, c) VALUES (?, ?, ?)') or die;

while (<>) {
    chomp;
    /^(\w+),(\w+),(\w+)$/ or next;
    $sth->execute($1, $2, $3);
}

Suggested fix:
See the attached patch.
[15 Feb 2007 1:55] [ name withheld ]
DBD-mysql-4.001.patch

Attachment: DBD-mysql-4.001.patch (text/x-patch), 33.55 KiB.

[21 Mar 2007 13:07] Valeriy Kravchuk
Thank you for a problem report and patch (and sorry for a delay with its processing). I tried to use your test case with latest DBD::mysql 4.003 and MySQL server 5.0.40-BK on Linux, and got no obvious problems:

openxs@suse:~/dbs/5.0> bin/mysql -uroot test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 33
Server version: 5.0.40 Source distribution

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> drop table t;
Query OK, 0 rows affected (0.01 sec)

mysql> create table t(a int, b int, c int);
Query OK, 0 rows affected (0.01 sec)

mysql> exit
Bye
openxs@suse:~/dbs/5.0> perl -w 26388.pl
1,2,3
4,5,6
openxs@suse:~/dbs/5.0> bin/mysql -uroot test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 35
Server version: 5.0.40 Source distribution

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> select * from t;
+------+------+------+
| a    | b    | c    |
+------+------+------+
|    1 |    2 |    3 |
|    4 |    5 |    6 |
+------+------+------+
2 rows in set (0.00 sec)

mysql> exit
Bye
openxs@suse:~/dbs/5.0> cat 26388.pl
use DBI;

my $dbh = DBI->connect('DBI:mysql:test;mysql_server_prepare=1', 'root',
'') or die;
my $sth = $dbh->prepare('INSERT t (a, b, c) VALUES (?, ?, ?)') or die;

while (<>) {
    chomp;
    /^(\w+),(\w+),(\w+)$/ or next;
    $sth->execute($1, $2, $3);
}

Plkease, check my test and inform me what shall I do differently to repeat the problem, and where is it?
[21 Mar 2007 22:16] [ name withheld ]
Hmm... It seems whether/when SIGSEGV occurs depends on the timing of garbage collection, etc.

How about this:

% ( echo 1,2,3 ; echo 4,5,6 ) | perl -w -I/path/to/patched/dbd -e'use strict; use DBI; \
my $dbh = DBI->connect("DBI:mysql:test;mysql_server_prepare=1", "usr", "pwd") or die; \
$dbh->do("CREATE TEMPORARY TABLE t (a int, b int, c int) ENGINE MEMORY") or die; \
my $sth = $dbh->prepare("INSERT t (a, b, c) VALUES (?, ?, ?)") or die; \
while (<>) { chomp; /^(\w+),(\w+),(\w+)$/ or next; $sth->execute($1, $2, $3); } \
$sth = $dbh->prepare("SELECT * FROM t") or die; \
$sth->execute; $sth->bind_columns(\$_[0], \$_[1], \$_[2]); print join(",", @_), "\n" while ($sth->fetch); \
$sth->execute; $sth->bind_columns(\$_[0], \$_[1], \$_[2]); print join(",", @_), "\n" while ($sth->fetch);'
1,2,3
4,5,6
1,2,3
4,5,6
% ( echo 1,2,3 ; echo 4,5,6 ) | perl -w -I/path/to/unpatched/dbd -e'use strict; use DBI; \
my $dbh = DBI->connect("DBI:mysql:test;mysql_server_prepare=1", "usr", "pwd") or die; \
$dbh->do("CREATE TEMPORARY TABLE t (a int, b int, c int) ENGINE MEMORY") or die; \
my $sth = $dbh->prepare("INSERT t (a, b, c) VALUES (?, ?, ?)") or die; \
while (<>) { chomp; /^(\w+),(\w+),(\w+)$/ or next; $sth->execute($1, $2, $3); } \
$sth = $dbh->prepare("SELECT * FROM t") or die; \
$sth->execute; $sth->bind_columns(\$_[0], \$_[1], \$_[2]); print join(",", @_), "\n" while ($sth->fetch); \
$sth->execute; $sth->bind_columns(\$_[0], \$_[1], \$_[2]); print join(",", @_), "\n" while ($sth->fetch);'
1,2,3
4,5,6
Segmentation fault (core dumped)
% dbx `which perl` core
For information about new features see `help changes'
To remove this message, put `dbxenv suppress_startup_message 7.5' in your .dbxrc
Reading perl
core file header read successfully
Reading ld.so.1
Reading libperl.so.1
Reading libdl.so.1
Reading libc.so.1
Reading libsocket.so.1
Reading libnsl.so.1
Reading libm.so.2
Reading DBI.so
Reading Util.so
Reading mysql.so
Reading librt.so.1
Reading libgen.so.1
Reading nss_files.so.1
program terminated by signal SEGV (no mapping at the fault address)
0xd1dd5202: t_splay+0x0022:     movl     0x00000008(%ebx),%ecx
Current function is mysql_st_internal_execute41
 3052     if (!(*result= mysql_stmt_result_metadata(stmt)))
(dbx) where
  [1] t_splay(0x8228fa0), at 0xd1dd5202 
  [2] t_delete(0x8228fa0), at 0xd1dd50dd 
  [3] realfree(0x822af20), at 0xd1dd4e1d 
  [4] cleanfree(0x0), at 0xd1dd5450 
  [5] _malloc_unlocked(0x58, 0x8226f30, 0x54, 0xd1ae4cfc, 0x82273f8, 0x8076060), at 0xd1dd4899 
  [6] malloc(0x54), at 0xd1dd47df 
  [7] my_malloc(0x54, 0x30), at 0xd1a5e6bf 
  [8] mysql_stmt_result_metadata(0x8227088), at 0xd1a5a265 
=>[9] mysql_st_internal_execute41(sth = 0x81968a4, num_params = 0, result = 0x8226fc0, stmt = 0x8227088, bind = (nil), has_been_bound = 0x8226fb8), line 3052 in "dbdimp.c"
  [10] mysql_st_execute(sth = 0x81968a4, imp_sth = 0x8226f30), line 3178 in "dbdimp.c"
  [11] XS_DBD__mysql__st_execute(cv = 0x80b6f34), line 569 in "mysql.xsi"
  [12] XS_DBI_dispatch(0x808f988), at 0xd1c1abb9 
  [13] Perl_pp_entersub(0x8046e18, 0xd1fa21a0, 0xd1f91000, 0x8046a78, 0xd1edf7ea, 0x8046cdc), at 0xd1f0d18a 
  [14] Perl_runops_standard(0x8046cdc, 0xd1f91000, 0xd1fa1e80, 0x8046a98, 0x8046cd0, 0xd1edf60d), at 0xd1f3a487 
  [15] S_run_body(0x1), at 0xd1edf7ea 
  [16] perl_run(0x8062360), at 0xd1edf60d 
  [17] main(0x4, 0x8046d20, 0x8046d34), at 0x80511d5
[13 Apr 2007 13:35] Valeriy Kravchuk
What version of Perl and DBD::MySQL you had used in your last example?
[13 Apr 2007 14:47] [ name withheld ]
Perl: v5.8.4

DBD::mysql: -I/path/to/patched/dbd:   v4.001 + my patch ( http://bugs.mysql.com/file.php?id=5653 )
            -I/path/to/unpatched/dbd: v4.003

By the way...

1. imp_sth->bind and imp_sth->fbind are initialized only in dbd_st_prepare().
2. Once the statement handle is used, imp_sth->bind and imp_sth->fbind
   are freed in dbd_st_finish() (by explicit or implicit $sth->finish).
3. Then the statement handle is reused, they are left freed (not initialized again)
   and used in dbd_bind_ph() and dbd_st_execute().

So, if they are freed in dbd_st_finish(), freed memory area will be used.
Whether SEGV occurs depends on whether the memory area is reclaimed by OS,
but using freed memory area is bad thing anyway.
[13 Jul 2007 16:09] Valeriy Kravchuk
Sorry for a delay with this bug report. Please, try to repeat with a newer version, 4.005, and inform about the results.
[13 Aug 2007 23:00] Bugs System
No feedback was provided for this bug for over a month, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".