write to freed buffer in dbd::mysql 3.0003_1
am 09.05.2006 14:34:37 von Martin.Evans
The following script causes a segfault due to writing to freed memory. I have
tracked down why it happens (see valgrind output) but not how to fix it yet
(although in the example, commenting out the finish works).
A make test of DBD::mysql 3.0003_1 shows this problem for me in the
50chopblanks test.
#!/usr/bin/perl -w
use DBI qw (neat_list);
my $dbh = DBI->connect('dbi:mysql:test','test');
$dbh->do(q/drop table if exists test/);
$dbh->do(q/create table test (a int, b char(64))/);
$dbh->do(q/insert into test values (1, 'one')/);
my $sth = $dbh->prepare(q/select * from test/);
$sth->execute;
while (my @row = $sth->fetchrow_array) {
print neat_list(\@row, 10, ",'"),"\n";
}
$sth->finish;
$sth->execute;
while (my @row = $sth->fetchrow_array) {
print neat_list(\@row, 10, ",'"),"\n";
}
==5361== Invalid write of size 4
==5361== at 0x45F7A21: setup_one_fetch_function (in
/usr/mysql/lib/mysql/libmysqlclient.so.15.0.0)
==5361== by 0x45F6348: mysql_stmt_execute (in
/usr/mysql/lib/mysql/libmysqlclient.so.15.0.0)
==5361== by 0x45DA925: mysql_st_internal_execute41 (dbdimp.c:2771)
==5361== by 0x45DAC39: mysql_st_execute (dbdimp.c:2894)
==5361== by 0x45DEF89: XS_DBD__mysql__st_execute (mysql.xsi:562)
==5361== by 0x43C20AC: XS_DBI_dispatch (in
/usr/local/lib/perl5/site_perl/5.8.7/i686-linux/auto/DBI/DBI .so)
==5361== by 0x80AD37E: Perl_pp_entersub (in /usr/bin/perl)
==5361== by 0x80A70EF: Perl_runops_standard (in /usr/bin/perl)
==5361== by 0x8061654: S_run_body (in /usr/bin/perl)
==5361== by 0x80613AD: perl_run (in /usr/bin/perl)
==5361== by 0x805E866: main (in /usr/bin/perl)
==5361== Address 0x45A7188 is 0 bytes inside a block of size 48 free'd
==5361== at 0x401B1DB: free (m_replacemalloc/vg_replace_malloc.c:235)
==5361== by 0x8099D5D: Perl_safesysfree (in /usr/bin/perl)
==5361== by 0x45D70A5: FreeFBuffer (dbdimp.c:197)
==5361== by 0x45DB8A7: mysql_st_finish (dbdimp.c:3378)
==5361== by 0x45DB286: mysql_st_fetch (dbdimp.c:3153)
==5361== by 0x45DF173: XS_DBD__mysql__st_fetchrow_array (mysql.xsi:610)
==5361== by 0x43C20AC: XS_DBI_dispatch (in
/usr/local/lib/perl5/site_perl/5.8.7/i686-linux/auto/DBI/DBI .so)
==5361== by 0x80AD37E: Perl_pp_entersub (in /usr/bin/perl)
==5361== by 0x80A70EF: Perl_runops_standard (in /usr/bin/perl)
==5361== by 0x8061654: S_run_body (in /usr/bin/perl)
==5361== by 0x80613AD: perl_run (in /usr/bin/perl)
==5361== by 0x805E866: main (in /usr/bin/perl)
==5361==
finish is throwing stuff away that the mysql client still has the address of.
I think the problem would be worse if the example was using bound columns.
Martin
--
Martin J. Evans
Easysoft Ltd, UK
http://www.easysoft.com
Bug in binding params in dbd::mysql 3.0003_1
am 11.05.2006 00:38:46 von henri
There's a bug in binding parameters in DBD::mysql 3.0003_1, when
binding a string that has slashes in it.
Below is how to reproduce it.
Create a simple table:
=== TABLE ===
CREATE TABLE tbl_tst_xx (
cv1 varchar(255) NULL,
ci2 int(11) NULL,
ci3 smallint(6) NULL
)
======
Then write a perl script that uses bound parameters:
=== SCRIPT ===
use strict;
use warnings;
use DBI qw(:sql_types);
use Data::Dumper;
my @dsn = ("dbi:mysql:database=XXX;host=localhost;port=3306;");
my $sql;
my $dbh;
my $sth;
my $results;
$dbh = DBI->connect( @dsn, 'username', 'password', {
PrintError => 1,
AutoCommit => 0,
RaiseError => 0,
ChopBlanks => 1
});
$sql = 'insert into tbl_tst_xx (cv1, ci2, ci3) values (?,?,?)';
$sth = $dbh->prepare( $sql );
$sth->bind_param(1, 'http://test/test/test.gif',
{ TYPE => SQL_VARCHAR});
$sth->bind_param(2, 1234567890, { TYPE => SQL_INTEGER });
$sth->bind_param(3, 0, { TYPE => SQL_INTEGER });
$sth->execute;
do {
$results = $sth->fetchall_arrayref;
foreach (@$results) {
warn join("\t", @$_)."\n";
}
} while ($sth->more_results);
$sth->finish;
======
Run the script and see what is inserted into the table.
You'll notice that the integers are completely off. If you run the
same test but put in the varchar field a simple string without the
slashes, you're ok.
If you run the same script but without bound parameters (and with
slashes), the insert is good.
RE: Bug in binding params in dbd::mysql 3.0003_1
am 11.05.2006 12:22:27 von Martin.Evans
Henri,
Firstly, bare in mind I've changed by 3.0003_1 to avoid the corruption bug I
posted earlier - I can supply a change for you but it is just currently a hack
to avoid the corruption.
I get:
> describe tbl_tst_xx;
Field:Type:Null:Key:Default:Extra:
cv1:varchar(255):YES::{NULL}::
ci2:int(11):YES::{NULL}::
ci3:smallint(6):YES::{NULL}::
DBD::mysql::st execute failed: Out of range value adjusted for column 'ci3' at
row 1 at z.pl line 15.
and I have:
sql_mode=traditional
If I omit the SQL_INTEGER off the bind param 3 call the execute succeeds. It
makes no difference to me whether the string contains / or not. With bind
param 3 changed I get:
xxxxxxxxxxxxxxxxxxxxxxxxa, 875770417, 0
and that is indeed what is in the database. If I remote the SQL_INTEGER off the
second bind param call I correctly get:
xxxxxxxxxxxxxxxxxxxxxxxxa, 1234567890, 0
So my guess it is more to do with setting the type to SQL_INTEGER because when
you do this dbd::mysql says they are integers and when you omit the SQL_INTEGER
dbd::mysql says they are strings (see a DBI trace).
Looking at dbdimp.c I cannot understand how this code ever worked. As I read
it, if you use TYPE => SQL_INTEGER, dbd::mysql sets the MYSQL_BIND
buffer_type to MYSQL_TYPE_LONG which is supposed to be a int C type but the
buffer points to a string. Are you saying using TYPE => SQL_INTEGER worked in
dbd::mysql at some time in the past?
Also, looking at DBD::ODBC, parameters are always bound as strings although the
ODBC driver might be asked to interpret them as integers as in the ValueType
and ParameterType arguments to SQLBindParameter.
If you omit the TYPE=>, it works for me.
BTW, I presume you missed the select from your example code.
Martin
--
Martin J. Evans
Easysoft Ltd, UK
http://www.easysoft.com
On 10-May-2006 Henri Asseily wrote:
> There's a bug in binding parameters in DBD::mysql 3.0003_1, when
> binding a string that has slashes in it.
> Below is how to reproduce it.
>
> Create a simple table:
>
> === TABLE ===
>
> CREATE TABLE tbl_tst_xx (
> cv1 varchar(255) NULL,
> ci2 int(11) NULL,
> ci3 smallint(6) NULL
> )
>
> ======
>
>
> Then write a perl script that uses bound parameters:
>
>
> === SCRIPT ===
>
> use strict;
> use warnings;
> use DBI qw(:sql_types);
> use Data::Dumper;
>
> my @dsn = ("dbi:mysql:database=XXX;host=localhost;port=3306;");
>
> my $sql;
> my $dbh;
> my $sth;
> my $results;
>
> $dbh = DBI->connect( @dsn, 'username', 'password', {
> PrintError => 1,
> AutoCommit => 0,
> RaiseError => 0,
> ChopBlanks => 1
> });
>
> $sql = 'insert into tbl_tst_xx (cv1, ci2, ci3) values (?,?,?)';
> $sth = $dbh->prepare( $sql );
> $sth->bind_param(1, 'http://test/test/test.gif',
> { TYPE => SQL_VARCHAR});
> $sth->bind_param(2, 1234567890, { TYPE => SQL_INTEGER });
> $sth->bind_param(3, 0, { TYPE => SQL_INTEGER });
> $sth->execute;
>
> do {
> $results = $sth->fetchall_arrayref;
> foreach (@$results) {
> warn join("\t", @$_)."\n";
> }
> } while ($sth->more_results);
> $sth->finish;
>
> ======
>
> Run the script and see what is inserted into the table.
> You'll notice that the integers are completely off. If you run the
> same test but put in the varchar field a simple string without the
> slashes, you're ok.
> If you run the same script but without bound parameters (and with
> slashes), the insert is good.