Skip to content
Permalink
Browse files

Fix use-after-free for repeated fetchrow_arrayref calls when mysql_se…

…rver_prepare=1

Function dbd_st_fetch() via Renew() can reallocate output buffer for
mysql_stmt_fetch() call. But it does not update pointer to that buffer in
imp_sth->stmt structure initialized by mysql_stmt_bind_result() function.
That leads to use-after-free in any mysql function which access
imp_sth->stmt structure (e.g. mysql_stmt_fetch()).

This patch fix this problem and properly updates pointer in imp_sth->stmt
structure after Renew() call.

Test 40server_prepare_crash.t is extended to check for that use-after-free
crash.
  • Loading branch information...
pali authored and mbeijen committed Nov 18, 2016
1 parent 2195ec6 commit 3619c170461a3107a258d1fd2d00ed4832adb1b1
Showing with 44 additions and 3 deletions.
  1. +2 −0 dbdimp.c
  2. +42 −3 t/40server_prepare_crash.t
@@ -4050,6 +4050,8 @@ dbd_st_fetch(SV *sth, imp_sth_t* imp_sth)
Renew(fbh->data, fbh->length, char);
buffer->buffer_length= fbh->length;
buffer->buffer= (char *) fbh->data;
imp_sth->stmt->bind[i].buffer_length = fbh->length;
imp_sth->stmt->bind[i].buffer = (char *)fbh->data;

if (DBIc_TRACE_LEVEL(imp_xxh) >= 2) {
int j;
@@ -10,11 +10,22 @@ require "t/lib.pl";
my $dbh = eval { DBI->connect($test_dsn, $test_user, $test_password, { PrintError => 1, RaiseError => 1, AutoCommit => 0, mysql_server_prepare => 1, mysql_server_prepare_disable_fallback => 1 }) };
plan skip_all => "no database connection" if $@ or not $dbh;

plan tests => 17;
plan tests => 39;

ok $dbh->do("CREATE TEMPORARY TABLE t (i INTEGER NOT NULL, n TEXT)");
my $sth;

ok my $sth = $dbh->prepare("SELECT * FROM t WHERE i=? AND n=?");
ok $dbh->do("CREATE TEMPORARY TABLE t (i INTEGER NOT NULL, n LONGBLOB)");

ok $sth = $dbh->prepare("INSERT INTO t(i, n) VALUES(?, ?)");
ok $sth->execute(1, "x" x 10);
ok $sth->execute(2, "x" x 100);
ok $sth->execute(3, "x" x 1000);
ok $sth->execute(4, "x" x 10000);
ok $sth->execute(5, "x" x 100000);
ok $sth->execute(6, "x" x 1000000);
ok $sth->finish();

ok $sth = $dbh->prepare("SELECT * FROM t WHERE i=? AND n=?");

ok $sth->bind_param(2, "x" x 1000000);
ok $sth->bind_param(1, "abcx", 12);
@@ -34,6 +45,34 @@ ok $sth = $dbh->prepare("SELECT 1 FROM t WHERE i = ?" . (" OR i = ?" x 10000));
ok $sth->execute((1) x (10001));
ok $sth->finish();

my $test;
ok $sth = $dbh->prepare("SELECT i,n FROM t WHERE i = ?");

ok $sth->execute(1);
ok $sth->fetchrow_arrayref();

ok $sth->execute(2);
$test = map { $_ } 'a';
ok $sth->fetchrow_arrayref();

ok $sth->execute(3);
$test = map { $_ } 'b' x 10000000; # try to reuse released memory
ok $sth->fetchrow_arrayref();

ok $sth->execute(4);
$test = map { $_ } 'cd' x 10000000; # try to reuse of released memory
ok $sth->fetchrow_arrayref();

ok $sth->execute(5);
$test = map { $_ } 'efg' x 10000000; # try to reuse of released memory
ok $sth->fetchrow_arrayref();

ok $sth->execute(6);
$test = map { $_ } 'hijk' x 10000000; # try to reuse of released memory
ok $sth->fetchrow_arrayref();

ok $sth->finish();

ok $dbh->do("SELECT 1 FROM t WHERE i = ?" . (" OR i = ?" x 10000), {}, (1) x (10001));

ok $dbh->disconnect();

0 comments on commit 3619c17

Please sign in to comment.
You can’t perform that action at this time.