|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2016-06-06 15:54 UTC] maggus dot staab at googlemail dot com
Description:
------------
time spent in session_start() is not subject to max_execution_time().
this can cause php to block all its worker processes by waiting for the session_lock, which finally leads to a no longer responding website.
shouldn't a process error and exit after max_execution_time-seconds when it is not able to aquire the session lock while session_start()?
running with php 5.4 (as this is the most recent version which ubuntu12lts provides)
PHP 5.4.45-3+deb.sury.org~precise+1 (cli) (built: Jan 7 2016 15:32:17)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies
with Zend Debugger v6.0.0, Copyright (c) 1999-2013, by Zend Technologies
with blackfire v1.10.6, https://blackfire.io, by Blackfireio Inc.
Test script:
---------------
<?php
// run the script 2x concurrently
set_time_limit(5);
echo "acquire session lock:". time() ."\n";
session_start();
echo "got session lock:". time()."\n";
sleep(10);
echo "finished:". time()."\n";
Expected result:
----------------
1st instance should report
>acquire session lock:1465227871
>got session lock:1465227871
>finished:1465227881
2nd instance should error after 5 seconds because session_start() is not able to aquire a session lock within 5 seconds
Actual result:
--------------
1st instance reports
>acquire session lock:1465227871
>got session lock:1465227871 // LOCK immediately acquired
>finished:1465227881
2nd instance reports
>acquire session lock:1465227872
>got session lock:1465227881 // LOCK acquired after 1st instance exit'ed
>finished:1465227891
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Fri Oct 24 04:00:01 2025 UTC |
put the following code into a file called "experiment.php" and you can reproduce the issue. with my custom session files handler (see comment above) registered $handler = new FileSessionHandler(200); session_set_save_handler($handler, true); the queued up requests immediately fail. with the native php-session handler 20 apache workers are are bound with just 1 request. depending on how "slow" you simulate the first 8 workers, the things get very fast a lot worse as the apache nearly needs forever to fullfill all the requests. if (!empty($_GET['worker'])) { $worker = $_GET['worker']; echo "<xmp>"; echo "ID: ". $worker."\n"; $start = microtime(true); echo "ACQUIRE LOCK: ". $start ."\n"; try { session_start(); } catch (Exception $e) { var_dump($e->getMessage()); exit(); } $waited = ((microtime(true) - $start) * 1000); // simulate work $sleep = 500; if ($worker < 8) { $sleep = 5000; } usleep($sleep * 1000); // make sure the server needs to update the sess file after each request $_SESSION['worker'][$worker]++; $finished = ((microtime(true) - $start) * 1000); echo "FINISHED: ". $finished ."ms\n"; // highlight runs which were blocked by others // (so session_start() couldn't return immediately) if ($waited > 10) { echo '</xmp><span style="color:red">'; } echo 'BLOCKED FOR '. $waited .'ms'; return; } foreach(range(1,20) as $workerId) { echo '<iframe src="experiment.php?worker='. $workerId .'"></iframe>'; }For those who would like to mitigate DoS attacks by session lock. Minimizing locked period works. i.e. use session_start('read_and_close'=>1), use session_commit() as soon as you've finished updating $_SESSION. Drawback is you may accidentally update $_SESSION while it's not active. Be careful for this!