Skip to content

Commit

Permalink
MDL-72670 session: Correct read only debugging logic
Browse files Browse the repository at this point in the history
Prior to this patch the debugging mode (when enabled) would trigger
on everywhere, regardless of whether or not READ_ONLY_SESSION is defined.

This patch modifies that behaviour so that the debugging only kicks in
if READ_ONLY_SESSION is defined and set to true.
  • Loading branch information
cameron1729 committed Oct 8, 2021
1 parent d2c4d59 commit 2de9208
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 5 deletions.
35 changes: 32 additions & 3 deletions lib/classes/session/manager.php
Expand Up @@ -60,6 +60,15 @@ class manager {
/** @var array Stores the the SESSION before a request is performed, used to check incorrect read-only modes */
private static $priorsession = [];

/**
* @var bool Used to trigger the SESSION mutation warning without actually preventing SESSION mutation.
* This variable is used to "copy" what the $requireslock parameter does in start_session().
* Once requireslock is set in start_session it's later accessible via $handler->requires_write_lock,
* When using $CFG->enable_read_only_sessions_debug mode, this variable serves the same purpose without
* actually setting the handler as requiring a write lock.
*/
private static $requireslockdebug;

/**
* If the current session is not writeable, abort it, and re-open it
* requesting (and blocking) until a write lock is acquired.
Expand All @@ -69,8 +78,16 @@ class manager {
* if the original session was not opened with the explicit intention of being locked,
* this will still restart your session so that code behaviour matches as closely
* as practical across environments.
*
* @param bool $readonlysession Used by debugging logic to determine if whatever
* triggered the restart (e.g., a webservice) declared
* itself as read only.
*/
public static function restart_with_write_lock() {
public static function restart_with_write_lock(bool $readonlysession) {
global $CFG;

self::$requireslockdebug = !$readonlysession;

if (self::$sessionactive && !self::$handler->requires_write_lock()) {
@self::$handler->abort();
self::$sessionactive = false;
Expand Down Expand Up @@ -108,6 +125,17 @@ public static function start() {
} else {
$requireslock = true; // For backwards compatibility, we default to assuming that a lock is needed.
}

// By default make the two variables the same. This means that when they are
// checked below in start_session and write_close there is no possibility for
// the debug version to "accidentally" execute the debug mode.
self::$requireslockdebug = $requireslock;
if (defined('READ_ONLY_SESSION') && !empty($CFG->enable_read_only_sessions_debug)) {
// Only change the debug variable if READ_ONLY_SESSION is declared,
// as would happen with the real requireslock variable.
self::$requireslockdebug = !READ_ONLY_SESSION;
}

self::start_session($requireslock);
}

Expand Down Expand Up @@ -138,9 +166,10 @@ private static function start_session(bool $requireslock) {
self::$sessionactive = true; // Set here, so the session can be cleared if the security check fails.
self::check_security();

if (!$requireslock || isset($CFG->enable_read_only_sessions_debug)) {
if (!$requireslock || !self::$requireslockdebug) {
self::$priorsession = (array) $_SESSION['SESSION'];
}

if (!empty($CFG->enable_read_only_sessions) && isset($_SESSION['SESSION']->cachestore_session)) {
$caches = join(', ', array_keys($_SESSION['SESSION']->cachestore_session));
$caches = str_replace('default_session-', '', $caches);
Expand Down Expand Up @@ -705,7 +734,7 @@ public static function write_close() {
self::sessionlock_debugging();

$requireslock = self::$handler->requires_write_lock();
if (!$requireslock || isset($CFG->enable_read_only_sessions_debug)) {
if (!$requireslock || !self::$requireslockdebug) {
// Compare the array of the earlier session data with the array now, if
// there is a difference then a lock is required.
$arraydiff = self::array_session_diff(
Expand Down
2 changes: 1 addition & 1 deletion lib/dml/tests/dml_read_slave_test.php
Expand Up @@ -427,6 +427,6 @@ public function test_sessions() : void {
$this->assertTrue($DB->perf_get_reads() > 0);
});

\core\session\manager::restart_with_write_lock();
\core\session\manager::restart_with_write_lock(false);
}
}
2 changes: 1 addition & 1 deletion lib/externallib.php
Expand Up @@ -198,7 +198,7 @@ public static function call_external_function($function, $args, $ajaxonly=false)
// Eventually this should shift into the various handlers and not be handled via config.
$readonlysession = $externalfunctioninfo->readonlysession ?? false;
if (!$readonlysession || empty($CFG->enable_read_only_sessions)) {
\core\session\manager::restart_with_write_lock();
\core\session\manager::restart_with_write_lock($readonlysession);
}

$currentpage = $PAGE;
Expand Down

0 comments on commit 2de9208

Please sign in to comment.