]> granicus.if.org Git - php/commitdiff
fix memory leak in pdo_pgsql closeCursor (bug 69752)
authorPhilip Hofstetter <phofstetter@sensational.ch>
Wed, 3 Jun 2015 12:56:02 +0000 (14:56 +0200)
committerAnatol Belski <ab@php.net>
Wed, 10 Jun 2015 14:36:00 +0000 (16:36 +0200)
the parent PDO closeCursor method resets the pdo_stmt_t's executed flag
which is used by the postgres driver as a flag to check whether to
allocate memory for the column data or not.

This means that after the parent closeCursor() has been called, the
pdo_pgsql driver will allocate a new buffer for the columns, so the
existing buffer should be freed when the cursor is being closed.

ext/pdo_pgsql/pgsql_statement.c
ext/pdo_pgsql/tests/bug69752.phpt [new file with mode: 0644]

index f541c86259a4a063789552230942dc689e91ed08..75c5a4a0419fba76209e2c8b622699c282af8571 100644 (file)
@@ -220,7 +220,7 @@ stmt_retry:
                return 0;
        }
 
-       if (!stmt->executed && !stmt->column_count) {
+       if (!stmt->executed && (!stmt->column_count || S->cols == NULL)) {
                stmt->column_count = (int) PQnfields(S->result);
                S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column));
        }
@@ -614,6 +614,12 @@ done:
 
 static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC)
 {
+       pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
+
+       if (S->cols != NULL){
+               efree(S->cols);
+               S->cols = NULL;
+       }
        return 1;
 }
 
diff --git a/ext/pdo_pgsql/tests/bug69752.phpt b/ext/pdo_pgsql/tests/bug69752.phpt
new file mode 100644 (file)
index 0000000..7d244ec
--- /dev/null
@@ -0,0 +1,54 @@
+--TEST--
+PDO PgSQL Bug #69752 (memory leak with closeCursor)
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) die('skip not loaded');
+require dirname(__FILE__) . '/config.inc';
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$pdo = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+
+$pdo->beginTransaction();
+
+$pdo->exec("
+    create table foo (
+        id bigserial not null primary key,
+        field1 text not null,
+        field2 text not null,
+        field3 text not null,
+        field4 int4 not null
+    )
+");
+$stmt = $pdo->prepare("insert into foo (field1, field2, field3, field4) values (:field1, :field2, :field3, :field4)");
+$max = 1000;
+$first_time_usage = null;
+
+for($i = 0; $i < $max; $i++) {
+    $data = array(
+        'field1' => "field1: $i",
+        'field2' => "field2: $i",
+        'field3' => "field3: $i",
+        'field4' => $i
+    );
+    $stmt->execute($data);
+    $stmt->closeCursor();
+    $usage = intval(memory_get_usage() / 1024);
+
+    if ($first_time_usage === null) $first_time_usage = $usage;
+
+    if ($first_time_usage != $usage){
+        printf("Memory Leak Detected: %d != %d\n", $usage, $first_time_usage);
+        break;
+    }
+}
+$pdo->rollBack();
+echo "done\n"
+?>
+--EXPECTF--
+done