Skip to content

and fix the use-after-free crash in RT #97625 #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 20, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion dbdimp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1996,6 +1996,7 @@ static int my_login(pTHX_ SV* dbh, imp_dbh_t *imp_dbh)
char* password;
char* mysql_socket;
int result;
int fresh = 0;
D_imp_xxh(dbh);

/* TODO- resolve this so that it is set only if DBI is 1.607 */
Expand Down Expand Up @@ -2044,12 +2045,15 @@ static int my_login(pTHX_ SV* dbh, imp_dbh_t *imp_dbh)
port ? port : "NULL");

if (!imp_dbh->pmysql) {
fresh = 1;
Newz(908, imp_dbh->pmysql, 1, MYSQL);
}
result = mysql_dr_connect(dbh, imp_dbh->pmysql, mysql_socket, host, port, user,
password, dbname, imp_dbh) ? TRUE : FALSE;
if (!result)
if (fresh && !result) {
/* Prevent leaks, but do not free in case of a reconnect. See #97625 */
Safefree(imp_dbh->pmysql);
}
return result;
}

Expand Down
105 changes: 61 additions & 44 deletions t/rt85919-fetch-lost-connection.t
Original file line number Diff line number Diff line change
@@ -1,44 +1,61 @@
use strict;
use warnings;
use DBI;
use Test::More;
use lib 't', '.';
use vars qw($table $test_dsn $test_user $test_password $mdriver);
require 'lib.pl';

my $dbh;
eval {$dbh= DBI->connect($test_dsn, $test_user, $test_password,
{ RaiseError => 1, PrintError => 0, AutoCommit => 0 });};
if ($@) {
plan skip_all => "ERROR: $@. Can't continue test";
}
my $sth;
my $ok = eval {
print "Connecting...\n";
ok( $sth = $dbh->do('SET wait_timeout = 5'), 'set wait_timeout');
print "Sleeping...\n";
sleep 7;
my $sql = 'SELECT 1';
if (1) {
ok( $sth = $dbh->prepare($sql), 'prepare SQL');
ok( $sth->execute(), 'execute SQL');
my @res = $sth->fetchrow_array();
is ( $res[0], undef, 'no rows returned');
ok( $sth->finish(), 'finish');
$sth = undef;
}
else {
print "Selecting...\n";
my @res = $dbh->selectrow_array($sql);
}
$dbh->disconnect();
$dbh = undef;
1;
};
if (not $ok) {
is ( $DBI::err, 2006, 'Received error 2006' );
is ( $DBI::errstr, 'MySQL server has gone away', 'Received MySQL server has gone away');
eval { $sth->finish(); } if defined $sth;
eval { $dbh->disconnect(); } if defined $dbh;
}
done_testing();
use strict;
use warnings;
use DBI;
use Test::More;
use lib 't', '.';
use vars qw($table $test_dsn $test_user $test_password $mdriver);
require 'lib.pl';

my $dbh;
eval {$dbh= DBI->connect($test_dsn, $test_user, $test_password,
{ RaiseError => 1, PrintError => 0, AutoCommit => 0 });};
if ($@) {
plan skip_all => "ERROR: $@. Can't continue test";
}
my $sth;
my $ok = eval {
print "Connecting...\n";
ok( $sth = $dbh->do('SET wait_timeout = 5'), 'set wait_timeout');
print "Sleeping...\n";
sleep 7;
my $sql = 'SELECT 1';
if (1) {
ok( $sth = $dbh->prepare($sql), 'prepare SQL');
ok( $sth->execute(), 'execute SQL');
my @res = $sth->fetchrow_array();
is ( $res[0], undef, 'no rows returned');
ok( $sth->finish(), 'finish');
$sth = undef;
}
else {
print "Selecting...\n";
my @res = $dbh->selectrow_array($sql);
}
$dbh->disconnect();
$dbh = undef;
1;
};
if (not $ok) {
is ( $DBI::err, 2006, 'Received error 2006' );
is ( $DBI::errstr, 'MySQL server has gone away', 'Received MySQL server has gone away');
eval { $sth->finish(); } if defined $sth;
eval { $dbh->disconnect(); } if defined $dbh;
}

if (0) {
# This causes the use=after-free crash in RT #97625.
# different testcase by killing the service. which is of course
# not doable in a general testscript and highly system dependent.
system(qw(sudo service mysql start));
use DBI;
my $dbh = DBI->connect("DBI:mysql:database=test:port=3306");
$dbh->{mysql_auto_reconnect} = 1; # without this is works
my $select = sub { $dbh->do(q{SELECT 1}) for 1 .. 10; };
$select->();
system qw(sudo service mysql stop);
$select->();
ok(1, "dbh did not crash on closed connection");
system(qw(sudo service mysql start));
}

done_testing();