yiisoft/yii

Potential race condition issue in CHttpSession::freeze()/unfreeze()

karelvasicek opened this issue · 10 comments

What steps will reproduce the problem?

  • 2 parallel requests (req1 and req2)
  • session opened not using Yii::app()->session->open() (we have mixed Yii1&Yii2 project and session is opened by Yii2 before session Yii1 core component is initialized)
  • cookieParams set in config so CHttpSession::setCookieParams() is called
  • call Yii::app()->session->...
  • freeze() called by setCookieParams()really freezes session as it is opened by Yii2 already
  • when session is frozen (closed, in fact) during processing req1, it can be opened by req2 and sessions get mixed

(Theoretical; it's hard to reproduce the issue and even harder to debug) process:

req1 starts
req1 opens session
req1 loads value_stored_in_session 'A'
req2 starts
(req2 cannot open session, it's locked by req1 => waits)
req1 freezes (closes) session (stores 'A' to _frozenData)
req2 opens session
(req1 cannot unfreeze session, it's locked by req2 => waits)
req2 loads value_stored_in_session 'A'
req2 freezes (closes) session (stores 'A' to _frozenData)
req1 unfreezes session
(req2 cannot unfreeze session, it's locked by req1 => waits)
req1 has value_stored_in_session 'A' in _frozenData
req1 changes value_stored_in_session to 'B'
req1 closes session
req1 saves value_stored_in_session 'B' !!!! (correct, expected)
req2 unfreezes session
req2 has value_stored_in_session 'A' in _frozenData !!!!
req2 closes session
req2 saves 'A' !!!!

What is the expected result?

$_SESSION['value_stored_in_session'] containing value 'B' after req1 is finished and this 'B' value loaded from session by req2.

What do you get instead?

Value 'A' stored in session.

Additional info

Q A
Yii version 1.1.25
PHP version 7.4.9
Operating system Fedora

@karelvasicek I'm not sure this scenario can really be fixed with framework code. Perhaps you should consider a different (session) storage solution?

@marcovtwout I don't think that different storage would make any difference, since the main problem here is that Yii stores session data in _frozenData, which may be outdated when session is unfreezed.

@marcovtwout

I'm not sure this scenario can really be fixed with framework code.

This is indeed a very rare scenario, but IMO the system should not allow req2 to open the session when req1 is processed ( = when session is frozen by req1), in general. flock()ing might help?

Perhaps you should consider a different (session) storage solution?

We worked it around by initiating Yii1 session component before session is open()ed by Yii2 so freezing/unfreezing is not performed any more.

Would this scenario be reproducable in a Yii 2 project as well? As far as I can tell it uses the same mechanism with "freezing/unfreezing" and keeping session data in memory when changing cookie params.

Would this scenario be reproducable in a Yii 2 project as well?

Yes, it's reproducible. I added sleep(60) to unfreeze() to be able to (reliably) start req2 in between freeze() and unfreeze() of req1 and used following actions


    public function actionReq1()
    {
        Yii::$app->session['var'] = 'A';
        Yii::$app->session->setName('A');
        return Yii::$app->session->id;
    }

    public function actionReq2()
    {
        Yii::$app->session['var'] = 'B';
        Yii::$app->session->setName('B');
        return Yii::$app->session->id;
    }

Steps to reproduce:

  1. use sleep(60) in unfreeze()
  2. open req1
  3. comment out sleep(60) in unfreeze()
  4. open req2
  5. req2 ends
  6. cat sess_... => __flash|a:0:{}var|s:1:"B";
  7. req1 ends
  8. cat sess_... => __flash|a:0:{}var|s:1:"A";

@karelvasicek Could you post this issue in the yii2 issue tracker as well? Development is more active there, and whatever fix they choose could then potentially be applied here.

@marcovtwout

Could you post this issue in the yii2 issue tracker as well?

yiisoft/yii2#19599 is created.

Is there a reason why one would call CHttpSession::setCookieParams() (or session_set_cookie_params()) after a session is started? Seems like this could be resolved by doing that before session_start() is called

I suggest to pursue this issue first in yiisoft/yii2#19599. As yii1 is in maintainance mode there is a better chance the issue will be fixed in yii2 first, and perhaps later we can backport it.

For now I'm marking this as a wontfix, unless yiisoft/yii2#19599 leads to a solution which can be backported.