Re: CakePHP ComboSession serious looping bug in documentation

2013-09-12 Thread Lionel Chan
This is resolved and patched in the documentation: 
https://github.com/cakephp/docs/commit/23fc03c12c3eea8456e895c2696444e037cdcc56

Thanks!
/Lionel

On Thursday, 12 September 2013 10:23:21 UTC+8, Lionel Chan wrote:
>
> Hi All
>
> *Environment:*
> CakePHP version: *2.4.0 stable*
> latest PHP 5.3
>
> We just recently pushed our app to AWS Beanstalk with ElastiCache + RDS as 
> our combo session, and using the ComboSession technique described in 
> here.
>  
> At first everything was ok, but then out of sudden, our app keep running 
> into some infinity loops that we can't really reproduce at that point of 
> time. What we can do is basically remove the cookie key from RDS, or clear 
> browser cookie. Clearly it has something to do with Session.
>
> So these are our sample error_logs:
>
> [Wed Sep 11 03:56:27 2013] [error] [client 10.251.91.107] PHP Fatal error: 
>  Maximum execution time of 60 seconds exceeded in 
> /var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
> [Wed Sep 11 04:00:49 2013] [error] [client 10.251.91.107] PHP Fatal error: 
>  Maximum execution time of 60 seconds exceeded in 
> /var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
> [Wed Sep 11 04:03:02 2013] [error] [client 10.251.91.107] PHP Fatal error: 
>  Maximum execution time of 60 seconds exceeded in 
> /var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
>  repeating 
> [Thu Sep 12 01:11:48 2013] [error] [client 10.251.91.107] PHP Fatal error: 
>  Maximum execution time of 60 seconds exceeded in 
> /var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
> [Thu Sep 12 01:13:50 2013] [error] [client 10.251.91.107] PHP Fatal error: 
>  Maximum execution time of 60 seconds exceeded in 
> /var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
> [Thu Sep 12 01:15:15 2013] [error] [client 10.251.91.107] PHP Fatal error: 
>  Allowed memory size of 268435456 bytes exhausted (tried to allocate 523800 
> bytes) in /var/app/current/lib/Cake/Model/Model.php on line 1368
>
> So apparently CakeSession is trying to session_start in a loop that it 
> can't resolve the session successfully.
>
> Then we found out one serious issue with the *ComboSession.php* mentioned 
> in the post 
> http://book.cakephp.org/2.0/en/development/sessions.html#creating-a-custom-session-handler,
>  
> that, let's say, if Cache lost the session data, and Cache::delete($id) 
> fails, parent::destroy() will simply won't be called to destroy expired 
> session.
>
> That says, we run into this kind of loop:
>
> *Let's assume Cache lost the session data, and RDS retained a piece of 
> expired session data:*
> 1. CakeSession::start()
> 2. CakeSession::_startSession() - session data get loaded from RDS because 
> Cache::read returns false
> 3. CakeSession::_checkValid()
> 4. CakeSession::_validAgentAndTime() - obviously timeout, we got an 
> invalid data here
> 5. CakeSession::destroy() - this calls destroy the session and trying to 
> call session_destroy()
> 6. ComboSession::destroy($id) - this calls attempted to destroy the 
> session data from Cache but failed (Memcache engine says not found!), and 
> since it's failed, parent::destroy() will never get triggered, and so *old 
> session data is survived*.
> 7. Go to step 1 and continues looping, and PHP will dies either the whole 
> memory get filled up or time out.
>
>
> To reproduce, we use this modified version of ComboSession to simulate the 
> problem: *(The following code is to demonstrate the bug, not a working 
> copy!)*
>
>  App::uses('DatabaseSession', 'Model/Datasource/Session');
>
> class ComboSession extends DatabaseSession implements 
> CakeSessionHandlerInterface {
> public $cacheKey;
>
> public function __construct() {
> $this->cacheKey = Configure::read('Session.handler.cache');
> parent::__construct();
> }
>
> // read data from the session.
> public function read($id) {
> $result = false; //simulate cache read fails
> if ($result) {
> return $result;
> }
> return parent::read($id);
> }
>
> // write data into the session.
> public function write($id, $data) {
> $result = Cache::write($id, $data, $this->cacheKey);
> if ($result) {
> return parent::write($id, $data);
> }
> return false;
> }
>
> // destroy a session.
> public function destroy($id) {
> $result = false; //simulate Cache::delete fails
> if ($result) {
> return parent::destroy($id);
> }
> return false;
> }
>
> // removes expired sessions.
> public function gc($expires = null) {
> return Cache::gc($this->cacheKey) && parent::gc($expires);
> }
> }
>
>
> And in the database, try modify your current session with time (in data), 
> and expires to a earlier time

CakePHP ComboSession serious looping bug in documentation

2013-09-12 Thread chaoszcat
Hi All

*Environment:*
CakePHP version: *2.4.0 stable*
latest PHP 5.3

We just recently pushed our app to AWS Beanstalk with ElastiCache + RDS as 
our combo session, and using the ComboSession technique described in 
here.
 
At first everything was ok, but then out of sudden, our app keep running 
into some infinity loops that we can't really reproduce at that point of 
time. What we can do is basically remove the cookie key from RDS, or clear 
browser cookie. Clearly it has something to do with Session.

So these are our sample error_logs:

[Wed Sep 11 03:56:27 2013] [error] [client 10.251.91.107] PHP Fatal error: 
 Maximum execution time of 60 seconds exceeded in 
/var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
[Wed Sep 11 04:00:49 2013] [error] [client 10.251.91.107] PHP Fatal error: 
 Maximum execution time of 60 seconds exceeded in 
/var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
[Wed Sep 11 04:03:02 2013] [error] [client 10.251.91.107] PHP Fatal error: 
 Maximum execution time of 60 seconds exceeded in 
/var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
 repeating 
[Thu Sep 12 01:11:48 2013] [error] [client 10.251.91.107] PHP Fatal error: 
 Maximum execution time of 60 seconds exceeded in 
/var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
[Thu Sep 12 01:13:50 2013] [error] [client 10.251.91.107] PHP Fatal error: 
 Maximum execution time of 60 seconds exceeded in 
/var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
[Thu Sep 12 01:15:15 2013] [error] [client 10.251.91.107] PHP Fatal error: 
 Allowed memory size of 268435456 bytes exhausted (tried to allocate 523800 
bytes) in /var/app/current/lib/Cake/Model/Model.php on line 1368

So apparently CakeSession is trying to session_start in a loop that it 
can't resolve the session successfully.

Then we found out one serious issue with the *ComboSession.php* mentioned 
in the post 
http://book.cakephp.org/2.0/en/development/sessions.html#creating-a-custom-session-handler,
 
that, let's say, if Cache lost the session data, and Cache::delete($id) 
fails, parent::destroy() will simply won't be called to destroy expired 
session.

That says, we run into this kind of loop:

*Let's assume Cache lost the session data, and RDS retained a piece of 
expired session data:*
1. CakeSession::start()
2. CakeSession::_startSession() - session data get loaded from RDS because 
Cache::read returns false
3. CakeSession::_checkValid()
4. CakeSession::_validAgentAndTime() - obviously timeout, we got an invalid 
data here
5. CakeSession::destroy() - this calls destroy the session and trying to 
call session_destroy()
6. ComboSession::destroy($id) - this calls attempted to destroy the session 
data from Cache but failed (Memcache engine says not found!), and since 
it's failed, parent::destroy() will never get triggered, and so *old 
session data is survived*.
7. Go to step 1 and continues looping, and PHP will dies either the whole 
memory get filled up or time out.


To reproduce, we use this modified version of ComboSession to simulate the 
problem: *(The following code is to demonstrate the bug, not a working 
copy!)*

cacheKey = Configure::read('Session.handler.cache');
parent::__construct();
}

// read data from the session.
public function read($id) {
$result = false; //simulate cache read fails
if ($result) {
return $result;
}
return parent::read($id);
}

// write data into the session.
public function write($id, $data) {
$result = Cache::write($id, $data, $this->cacheKey);
if ($result) {
return parent::write($id, $data);
}
return false;
}

// destroy a session.
public function destroy($id) {
$result = false; //simulate Cache::delete fails
if ($result) {
return parent::destroy($id);
}
return false;
}

// removes expired sessions.
public function gc($expires = null) {
return Cache::gc($this->cacheKey) && parent::gc($expires);
}
}


And in the database, try modify your current session with time (in data), 
and expires to a earlier time. You should run into infinity loop in the 
next run.

---

In the end, we fix it by issue delete on both Cache and RDS session data 
regardless if Cache succeed or not:

public function destroy($id) {
//Delete, regardless of cache succeed or not (so to prevent infinity loop)
Cache::delete($id, $this->cacheKey);
return parent::destroy($id);
}


*Suggestion: To prevent someone from having this issue again, I think it's 
best if you can update your documentation at *
http://book.cakephp.org/2.0/en/development/sessions.html#creating-a-custom-session-handler
.

Thanks!
/Lionel 

-- 
Like Us on Fac