Uploaded image for project: 'Moodle'
  1. Moodle
  2. MDL-77642

The configuration max_execution_time is being ignored in a scenario

XMLWordPrintable

    • MOODLE_400_STABLE

      Hi team, there seems to be a scenario in which the value of the PHP configuration `max_execution_time` and possibly `$CFG->maxtimelimit` are being ignored, in particular, if the value on those configs is greater than 300 then the script 'theme/styles.php' ignores those configs, but if the value is less than 300 then that value is accepted and used as the new timeout value.

      If the purpose of `$CFG->maxtimelimit` is to limit the amount of time that the php time limit can be raised, then I think the `max_execution_time` should not be ignored, or a new $CFG flag should be created to act as the `max_execution_time` (something like `$CFG->mintimelimit`). And also if this is the purpose, please skip the first two examples given, because they are given with the assumption that that is not the purpose of that flag.

      I've tested this problem with the execution of 'theme/styles.php', but this can happen to any script that uses `\theme_config::post_process` that can be found in 'lib/outputlib.php:1819' (on the 'master' branch). This can also happen more broadly with the usage of `core_php_time_limit::raise`.

      Here is the relevant code inside of  `\theme_config::post_process`:

      $needsparsing = !empty($treeprocessor) || !empty($this->rtlmode);
      if ($needsparsing) {
          // We might need more memory/time to do this, so let's play safe.
          raise_memory_limit(MEMORY_EXTRA);
          core_php_time_limit::raise(300); 

      The last line is the important one for this problem. The call to `core_php_time_limit::raise(300);` causes `max_execution_time` and `$CFG->maxtimelimit` to be ignored when they are greater than 300. Any value passed as argument to `core_php_time_limit::raise` will cause this behavior, for example `core_php_time_limit::raise(200)` will cause `$CFG->maxtimelimit` to be ignored when it's greater than 200.

      The problem seems to be that the argument passed to `core_php_time_limit::raise` is capable of overriding the value of `max_execution_time` and `$CFG->maxtimelimit`. Here is the relevant code inside of `core_php_time_limit::raise`:

      if (!empty($CFG->maxtimelimit)) {
          $realtimeout = max(1, $CFG->maxtimelimit);
          if ($newlimit === 0) {
              $newlimit = $realtimeout;
          } else {
              $newlimit = min($newlimit, $realtimeout);
          }
      } 

      For proper context, the value of `$newlimit` is the value passed as argument to `core_php_time_limit::raise`, so in the call `core_php_time_limit::raise(300)` then `$newlimit = 300`.

       

      First check the line:

      $realtimeout = max(1, $CFG->maxtimelimit); 

      This causes `$realtimeout = $CFG->maxtimelimit` when `$CFG->maxtimelimit` is greater than 0.

       

      Assuming `$newlimit != 0`, as in this cases where `$newlimit = 300`, then:

      $newlimit = min($newlimit, $realtimeout);

      Now I'll create two examples to show how this problem occurs:

       

      Example 1: `$CFG->maxtimelimit = 500`, `$newlimit = 300`, so:

      $newlimit = min($newlimit, $realtimeout);
      $newlimit = min(300, 500);
      $newlimit = 300; 

      As you can see, this has the effect of ignoring the value of `$CFG->maxtimelimit`. But in the opposite scenario it's not ignored:

       

      Example 2: `$CFG->maxtimelimit = 15`, `$newlimit = 300`, so:

      $newlimit = min($newlimit, $realtimeout);
      $newlimit = min(300, 15);
      $newlimit = 15;  

      In this case, the value of `$CFG->maxtimelimit` is respected.

       

      This means that `$CFG->maxtimelimit` is only respected when it is less than the value of the argument passed to `core_php_time_limit::raise`. This seems to be a confusing design, because sometimes `$CFG->maxtimelimit` will be ignored, and sometimes don't, depending on the value used for that configuration value.

      What should be the expected behavior of `$CFG->maxtimelimit`? Should it be always ignored when calling `core_php_time_limit::raise`? Or should `$CFG->maxtimelimit` take precedence over the argument passed to `core_php_time_limit::raise`? If it should always be ignored, then I think there should be a new CFG that is not ignored, for processes that need a higher timeout when raising the php time limit.

      About max_execution_time

      This code also has another problem (or maybe the main or only problem). When you go to the 'php.ini' and set the value of `max_execution_time` to a value higher than the argument passed to `core_php_time_limit::raise`, then the value of `max_execution_time` is also ignored. This can lead to situations in which `core_php_time_limit::raise` doesn't raise the php time limit, but actually reduces it. For example if `max_execution_time = 1800` and after 5 seconds of execution you call `core_php_time_limit::raise(100)` thinking you will add 100 seconds to the timeout, in reality the timeout won't be 1900 seconds as expected from raising the time limit. Instead the timeout will be 100, and the total execution time won't go beyond 105 seconds. This in effect causes `max_execution_time` to be ignored as well. The mechanism by which `max_execution_time` gets ignored is a little different, but the end effect is the same:

      Example 3: Say that `max_execution_time = 1500` and you call `core_php_time_limit::raise(300)`. Inside of that call, there ends up being a call to `set_time_limit(300)`. This resets the timeout and overrides the value of `max_execution_time`. I posit that the proper call could be `set_time_limit(1800)`, or maybe `set_time_limit(1800 - $timefromstart)` so that the php time limit is raised over the expected timeout of `max_execution_time`. This would give more control to the server sysadmins over slow processes, because without it they can't make use of `max_execution_time` when `core_php_time_limit::raise` is called.

      Lastly, as you can imagine, this problem produces the error "Fatal error: Maximum execution time of 300 seconds exceeded" to appear in the error.log from Apache, regardless of which value you set for `max_execution_time`.

      Expected behavior

      The value of `max_execution_time` is not ignored, and the call to `core_php_time_limit::raise` raises the php time limit over that value. Or a new CFG flag acts as `max_execution_time`.

      Observed behavior

      The values of `max_execution_time` and `$CFG->maxtimelimit` are being ignored when calling `core_php_time_limit::raise` with an argument that is less than those values.

            Unassigned Unassigned
            julian.tovar Julian Tovar
            Votes:
            1 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved:

                Error rendering 'clockify-timesheets-time-tracking-reports:timer-sidebar'. Please contact your Jira administrators.