CodeIgniter: "Cannot call session save handler in a recursive manner"

I am getting a timeout in Session_files_driver.phpwith these in my logs:

Severity: Error --> Maximum execution time of 30 seconds exceeded /system/libraries/Session/drivers/Session_files_driver.php 212
Severity: Warning --> Unknown: Cannot call session save handler in a recursive manner Unknown 0
Severity: Warning --> Unknown: Failed to write session data using user defined save handler. (session.save_path: /tmp/php) Unknown 0

What I am doing:

  • A view contains a form that posts data to one controller
  • The same view performs 2 asynchronous ajax calls to separate and different controllers when dom ready.
  • No problem so far
  • Data is posted from the form to controller
  • This controller, when detects the post, sets some flashdata and redirects back to same method
  • When hitting the view again from the redirect, only one ajax call is fired, the other hangs and hits the errors above.

Additional info:

  • Removing one ajax call, the operation is fine and it doesn’t matter which one is removed.
  • Removing the set flash data, things are fine
  • Removing the redirect, things are fine

I have separated out the barebones setup of this in a new 3.19 and zipped up here to see, just hit the form submit on the form field on the welcome view.

https://www.dropbox.com/s/2mbkdp1cdhftafj/ci-issue.zip?dl=0

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 6
  • Comments: 42 (24 by maintainers)

Commits related to this issue

Most upvoted comments

Problem is filesize return cached result (system/libraries/Session/drivers/Session_files_driver.php:210). clearstatcache(); before this line -> solves this issue, thx @it-can.

No, #5550 doesn’t resolve anything.

Anyway, I assume @Xcreen has no intention of replying or is just unavailable, but he did stumble upon a clue …

validateId() is supposed to be auto-called by PHP 7+ only if the handler implements SessionUpdateTimestampHandlerInterface, which it doesn’t because that’s fucking ridiculous, poorly documented and I’d told the guy who wrote that logic that it’s a bad idea, but he listens to nobody but the voices in his head.
The security researchers who reported the issue that warranted b3f7aae1079e8e484437bc67f4c126f34e7903d8 were supposed to verify it, and they didn’t catch that either.

Yet, validateId() is somehow being auto-called and triggering those errors. The messages are confusing because that’s what tends to happen when the bug is in PHP itself and it somehow doesn’t straight-up crash with a segmentation fault.

So, a logical solution should be to rename validateId() to something else and then drop the version checks so that CI always calls it:

diff --git a/system/libraries/Session/Session_driver.php b/system/libraries/Session/Session_driver.php
index 2fe30b8..a26b3a3 100644
--- a/system/libraries/Session/Session_driver.php
+++ b/system/libraries/Session/Session_driver.php
@@ -121,7 +121,7 @@ abstract class CI_Session_driver implements SessionHandlerInterface {
 	 */
 	public function php5_validate_id()
 	{
-		if (PHP_VERSION_ID < 70000 && isset($_COOKIE[$this->_config['cookie_name']]) && ! $this->validateId($_COOKIE[$this->_config['cookie_name']]))
+		if (isset($_COOKIE[$this->_config['cookie_name']]) && ! $this->validateSessionId($_COOKIE[$this->_config['cookie_name']]))
 		{
 			unset($_COOKIE[$this->_config['cookie_name']]);
 		}
diff --git a/system/libraries/Session/drivers/Session_database_driver.php b/system/libraries/Session/drivers/Session_database_driver.php
index 074accf..f342690 100644
--- a/system/libraries/Session/drivers/Session_database_driver.php
+++ b/system/libraries/Session/drivers/Session_database_driver.php
@@ -353,7 +353,7 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan
 	 * @param	string	$id
 	 * @return	bool
 	 */
-	public function validateId($id)
+	public function validateSessionId($id)
 	{
 		// Prevent previous QB calls from messing with our queries
 		$this->_db->reset_query();
diff --git a/system/libraries/Session/drivers/Session_files_driver.php b/system/libraries/Session/drivers/Session_files_driver.php
index 654f300..8c7fac0 100644
--- a/system/libraries/Session/drivers/Session_files_driver.php
+++ b/system/libraries/Session/drivers/Session_files_driver.php
@@ -402,7 +402,7 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle
 	 * @param	string	$id
 	 * @return	bool
 	 */
-	public function validateId($id)
+	public function validateSessionId($id)
 	{
 		return is_file($this->_file_path.$id);
 	}
diff --git a/system/libraries/Session/drivers/Session_memcached_driver.php b/system/libraries/Session/drivers/Session_memcached_driver.php
index 9e81168..b464439 100644
--- a/system/libraries/Session/drivers/Session_memcached_driver.php
+++ b/system/libraries/Session/drivers/Session_memcached_driver.php
@@ -303,7 +303,7 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa
 	 * @param	string	$id
 	 * @return	bool
 	 */
-	public function validateId($id)
+	public function validateSessionId($id)
 	{
 		$this->_memcached->get($this->_key_prefix.$id);
 		return ($this->_memcached->getResultCode() === Memcached::RES_SUCCESS);
diff --git a/system/libraries/Session/drivers/Session_redis_driver.php b/system/libraries/Session/drivers/Session_redis_driver.php
index d7a5755..f351aff 100644
--- a/system/libraries/Session/drivers/Session_redis_driver.php
+++ b/system/libraries/Session/drivers/Session_redis_driver.php
@@ -323,7 +323,7 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle
 	 * @param	string	$id
 	 * @return	bool
 	 */
-	public function validateId($id)
+	public function validateSessionId($id)
 	{
 		return (bool) $this->_redis->exists($this->_key_prefix.$id);
 	}

Can you all confirm that this works for you?