php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #57297 oci8 uses wrong permanent connection (ORA-00942)
Submitted: 2006-10-16 06:55 UTC Modified: 2006-10-18 10:23 UTC
From: jfbustarret at tf1 dot fr Assigned:
Status: Closed Package: oci8 (PECL)
PHP Version: 5.1.6 OS: Linux
Private report: No CVE-ID: None
 [2006-10-16 06:55 UTC] jfbustarret at tf1 dot fr
Description:
------------
On an apache webserver hosting various websites & connecting to various databases, we randomly get ORA-00942 errors (table or view does not exist).
It looks like the oci8 mixes up persistent connections & uses one with the wrong credentials.

Disabling persistent connections (oci8.max_persistent=0) solves the problem. Playing with ping_interval & persistent_timeout does not.

oci8 version : 1.2.2 ($Revision: 1.269.2.16.2.21 $)
Oracle client version : 9.2
PHP Version : 5.1.6 with hardening patch

Reproduce code:
---------------
No reproduce code. The errors are random (random times, random sql requests, random websites).


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2006-10-16 09:01 UTC] tony2001 at phpclub dot net
Can't reproduce.
 [2006-10-17 10:07 UTC] jfbustarret at tf1 dot fr
I did add debug messages in oci8.c

diff -rup oci8-1.2.2/oci8.c oci8-1.2.2.debug/oci8.c
--- oci8-1.2.2/oci8.c   2006-08-24 14:05:19.000000000 +0200
+++ oci8-1.2.2.debug/oci8.c     2006-10-17 15:26:59.000000000 +0200
@@ -666,7 +666,7 @@ PHP_MINFO_FUNCTION(oci)

        php_info_print_table_start();
        php_info_print_table_row(2, "OCI8 Support", "enabled");
-       php_info_print_table_row(2, "Version", "1.2.1");
+       php_info_print_table_row(2, "Version", "1.2.2.debug");
        php_info_print_table_row(2, "Revision", "$Revision: 1.269.2.16.2.21 $");

        sprintf(buf, "%ld", OCI_G(num_persistent));
@@ -1106,12 +1106,13 @@ php_oci_connection *php_oci_do_connect_e

                                                        /* okay, the connection is open and the server is still alive */
                                                        connection->used_this_request = 1;
-                                                       smart_str_free_ex(&hashed_details, 0);
                                                        if (zend_list_find(connection->rsrc_id, &rsrc_type) && (rsrc_type == le_pconnection) && zend_list_addref(connection->rsrc_id) == SUCCESS) {
                                                                /* do nothing */
                                                        } else {
                                                                connection->rsrc_id = zend_list_insert(connection, le_pconnection);
                                                        }
+                                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "found persistent connection for %s : %s (rsrc_id %d)\n", hashed_details.c, connection->hash_key, connection->rsrc_id);
+                                                       smart_str_free_ex(&hashed_details, 0);
                                                        return connection;
                                                }
                                        }

These debug message show different persistent connections using the same rsrc_id :
Oct 17 16:00:35  [error] 2880 - www.olweb.fr - Environnement - Warning PHP : ociplogon() [<a href='function.ociplogon'>function.ociplogon</a>]: found persistent connection for oci8___[user1]__1442677164__[database].world__8710 : oci8___[user1]__1442677164__[database].world__8710 (rsrc_id 56)
Oct 17 16:00:35  [error] 2880 - www.olweb.fr - Environnement - Warning PHP : ociplogon() [<a href='function.ociplogon'>function.ociplogon</a>]: found persistent connection for oci8___[user2]__2782104700__[database].world__8710 : oci8___[user2]__2782104700__[database].world__8710 (rsrc_id 56)

After this rsrc_id mixup, the db requests for the second connection use the first one.
 [2006-10-17 10:14 UTC] tony2001 at phpclub dot net
That doesn't help much in reproducing it.
 [2006-10-17 11:24 UTC] jfbustarret at tf1 dot fr
Tell me what you think of this scenario :

pconnect user1 => rsrc_id1
statement_free (or lob_free) => rsrc_id1 is removed from the list (zend_list_delete), but the persistent connection still exists and points to rsrc_id1
pconnect user2 => rsrc_id1 (zend_list_insert returns the first available)

The next pconnect user1 returns rsrc_id1, checks that it is a persistent connection (which it is, but it points to user2), and returns rsrc_id1. Hence the mixup.

Suggested fix : remove zend_list_delete(xxx->connection->rsrc_id) from php_oci_lob_free and php_oci_statement_free (which might have an impact on PECL bug #5571 (oci_new_connect() not closed by oci_close()))

I'll test the fix tomorrow.
 [2006-10-17 11:38 UTC] jfbustarret at tf1 dot fr
Reproduce code :

<?php
ini_set("display_errors", "On");
$server = "...";
$passwd1 = $user1 = "...";
$passwd2 = $user2 = "...";
$table1 = "...";

$conn1 = OCIPLogon($user1, $passwd1, $server);
$stmt = OCIParse($conn1, "select * from $table1");
OCIExecute($stmt);
$erra=OCIError($conn1);
if (!empty($erra['message']))
        print "Error: ${erra['code']} ${erra['message']}";
OCIFreeStatement($stmt);
$conn1 = OCIPLogon($user2, $passwd2, $server);
$stmt = OCIParse($conn1, "select * from $table1");
OCIExecute($stmt);
$erra=OCIError($conn1);
if (!empty($erra['message']))
        print "Error: ${erra['code']} ${erra['message']}";

Displays :

Warning: ociexecute(): ORA-00942: table or view does not exist in /mnt/home_distant/jfb/testoci.php on line 17

Expected result :

no error
 [2006-10-17 11:40 UTC] jfbustarret at tf1 dot fr
My mistake. The code was wrong...
 [2006-10-17 13:12 UTC] tony2001 at phpclub dot net
No bug -> bogus.
 [2006-10-18 03:15 UTC] jfbustarret at tf1 dot fr
The bug is not bogus. Only my reproduce code is.

Current oci8 does not garantee that different persistent connctions have different rsrc_id. This means that requests can use a connection with wrong credentials.

I'll try to write a new reproduce code and/or a fix.
 [2006-10-18 03:16 UTC] jfbustarret at tf1 dot fr
reopening
 [2006-10-18 05:11 UTC] tony2001 at phpclub dot net
>Current oci8 does not garantee that different persistent
>connctions have different rsrc_id. 
Wrong.

If you're unable to provide the reproduce code - the report is bogus.
 [2006-10-18 05:11 UTC] jfbustarret at tf1 dot fr
New reproduce code :

<?php
ini_set("display_errors", "On");
$server = "...";
$passwd1 = $user1 = "...";
$passwd2 = $user2 = "...";
$passwd3 = $user3 = "...";
$table1 = "...";
$table3 = "...";

// generate a random rsrc_id offset
$conn3 = OCIPLogon($user3, $passwd3, $server);
for ($i=1; $i<rand(1,2); $i++) {
        $stmt = OCIParse($conn3, "select * from $table3");
        OCIExecute($stmt);
}

// conn1 will sometimes get conn2->rsrc_id
$conn2 = OCIPLogon($user2, $passwd2, $server);
$conn1 = OCIPLogon($user1, $passwd1, $server);

// this will be randomly executed with user1 or user2 credentials
$stmt = OCIParse($conn1, "select * from $table1");
OCIExecute($stmt);

You have to call this multiple times through the HTTP server (apache in my case).
The script should not generate an error, but randomly displays :
<b>Warning</b>:  ociexecute() [<a href='function.ociexecute'>function.ociexecute</a>]: ORA-00942: table or view does not exist in <b>/data/www/exploit/htdocs/testoci.php</b> on line <b>23</b>

My understanding of the bug is that EG(regular_list) & all ressource ids are reset between requests. EG(persistent_list) is not reset, and connections store invalid rsrc_ids from previous requests. Shouldn't all persistent connections rsrc_ids be reset between requests ?
 [2006-10-18 05:58 UTC] jfbustarret at tf1 dot fr
Reverting to the oci8 version 1.1 way of using rsrc_id seems to solve the problem (reproduce code works without error, no error on the production platform for the last 30 minutes - 1 or 2 errors/minute before).

--- oci8-1.2.2/oci8.c   2006-08-24 14:05:19.000000000 +0200
+++ oci8-1.2.2-jfb/oci8.c       2006-10-18 11:16:34.000000000 +0200
@@ -1107,11 +1107,7 @@ php_oci_connection *php_oci_do_connect_e
                                                        /* okay, the connection is open and the server is still alive */
                                                        connection->used_this_request = 1;
                                                        smart_str_free_ex(&hashed_details, 0);
-                                                       if (zend_list_find(connection->rsrc_id, &rsrc_type) && (rsrc_type == le_pconnection) && zend_list_addref(connection->rsrc_id) == SUCCESS) {
-                                                               /* do nothing */
-                                                       } else {
-                                                               connection->rsrc_id = zend_list_insert(connection, le_pconnection);
-                                                       }
+                                                       connection->rsrc_id = zend_list_insert(connection, le_pconnection);
                                                        return connection;
                                                }
                                        }

Thanks for your help.
 [2006-10-18 06:03 UTC] tony2001 at phpclub dot net
Please get the latest 5.2 snapshot, apply this patch:
http://tony2001.phpclub.net/dev/tmp/pecl_bug9061.diff
(cd php-...; patch -p0 < /path/to/pecl_bug9061.diff)
and check if you still can reproduce it.
 [2006-10-18 09:59 UTC] jfbustarret at tf1 dot fr
Patch applied on PHP 5.1.6/oci8 1.2.2.

It crashes on :
hash_key_len = tmp ? strlen(tmp->hash_key) : 0;
(gdb) print *tmp
$3 = {env = 0x2, charset = 33656, server = 0x0, svc = 0x838f424, session = 0x838ed5c, err = 0x847ca58, errcode = 24, descriptors = 0x8480820, is_open = 0,
  is_attached = 0, is_persistent = 0, used_this_request = 0, needs_commit = 0, rsrc_id = 0, idle_expiry = 47, next_ping = 65537,
  hash_key = 0xb28863f1 <Address 0xb28863f1 out of bounds>}

After applying this after your patch, it works :

@@ -1108,9 +1108,9 @@ php_oci_connection *php_oci_do_connect_e
                                                        /* okay, the connection is open and the server is still alive */
                                                        connection->used_this_request = 1;
                                                        tmp = (php_oci_connection *)zend_list_find(connection->rsrc_id, &rsrc_type);
-                                                       hash_key_len = tmp ? strlen(tmp->hash_key) : 0;
+                                                       hash_key_len = tmp && rsrc_type == le_pconnection ? strlen(tmp->hash_key) : 0;

-                                                       if (tmp != NULL && rsrc_type == le_pconnection && hash_key_len == hashed_details.len &&
+                                                       if (hash_key_len > 0 && hash_key_len == hashed_details.len &&
                                                                memcmp(tmp->hash_key, hashed_details.c, hashed_details.len) == 0 && zend_list_addref(connection->rsrc_id) == SUCCESS) {
                                                                /* do nothing */
                                                        } else {
 [2006-10-18 10:08 UTC] tony2001 at phpclub dot net
So it crashes or it works?
 [2006-10-18 10:14 UTC] jfbustarret at tf1 dot fr
- It crashes with your patch

- It works fine (no crashes, no ORA-00942) with your patch AND my last patch (rsrc_type == le_pconnection moved before strlen(tmp->hash_key))
 [2006-10-18 10:23 UTC] tony2001 at phpclub dot net
This bug has been fixed in CVS.

In case this was a documentation problem, the fix will show up at the
end of next Sunday (CET) on pecl.php.net.

In case this was a pecl.php.net website problem, the change will show
up on the website in short time.
 
Thank you for the report, and for helping us make PECL better.


 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Dec 26 10:01:29 2024 UTC