Implement wall clock time limits for shell commands
authorTim Starling <tstarling@wikimedia.org>
Sun, 13 Jan 2013 22:48:03 +0000 (09:48 +1100)
committerTim Starling <tstarling@wikimedia.org>
Sun, 13 Jan 2013 22:53:59 +0000 (09:53 +1100)
* Use the "timeout" command to implement wall clock time limits.
* Write a message to stderr when the time limit is exceeded, for
consistency with ulimit
* Configure with $wgMaxShellWallClockTime
* If "time" is specified in $limits, use that as the wall clock limit in
preference to the global variable, to support legacy callers which
usually want to disable the limits.
* Remove the requirement for ulimit5.sh to be executable since this is
not necessary when you specify the interpreter. Should avoid subtle
problems when the executable bit is accidentally stripped.
* Move the interpretation of 0/-1/false limits to the shell script, so
that e.g. wall clock time can be limited even if memory is unlimited.

Change-Id: Id9f2fea7c3b027565bdc33b88a1a7d4cdfe43a6c

RELEASE-NOTES-1.21
bin/ulimit4.sh [deleted file]
bin/ulimit5.sh [new file with mode: 0644]
includes/DefaultSettings.php
includes/GlobalFunctions.php

index ee49e09..b8f3ba6 100644 (file)
@@ -84,6 +84,8 @@ production.
   in MW 1.20 (saving preferences using Special:Preferences cleared any
   additional fields) and which has been disabled in 1.20.1 as a part of
   a security fix (bug 42202).
+* Added the ability to limit the wall clock time used by shell processes, 
+  as well as the CPU time. Configurable with $wgMaxShellWallClockTime.
 
 === Bug fixes in 1.21 ===
 * (bug 40353) SpecialDoubleRedirect should support interwiki redirects.
diff --git a/bin/ulimit4.sh b/bin/ulimit4.sh
deleted file mode 100755 (executable)
index 2a840d2..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-ulimit -t $1 -v $2 -f $3
-eval "$4"
diff --git a/bin/ulimit5.sh b/bin/ulimit5.sh
new file mode 100644 (file)
index 0000000..3f24172
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+if [ "$1" -gt 0 ]; then
+       ulimit -t "$1"
+fi
+if [ "$2" -gt 0 ]; then
+       ulimit -v "$2"
+fi
+if [ "$3" -gt 0 ]; then
+       ulimit -f "$3"
+fi
+if [ "$4" -gt 0 ]; then
+       timeout $4 /bin/bash -c "$5"
+       STATUS="$?"
+       if [ "$STATUS" == 124 ]; then
+               echo "ulimit5.sh: timed out." 1>&2
+       fi
+       exit "$STATUS"
+else
+       eval "$5"
+fi
index d254ef6..f41c7aa 100644 (file)
@@ -6108,10 +6108,16 @@ $wgMaxShellMemory = 102400;
 $wgMaxShellFileSize = 102400;
 
 /**
- * Maximum CPU time in seconds for shell processes under linux
+ * Maximum CPU time in seconds for shell processes under Linux
  */
 $wgMaxShellTime = 180;
 
+/**
+ * Maximum wall clock time (i.e. real time, of the kind the clock on the wall
+ * would measure) in seconds for shell processes under Linux
+ */
+$wgMaxShellWallClockTime = 180;
+
 /**
  * Executable path of the PHP cli binary (php/php5). Should be set up on install.
  */
index 28cc001..0b1bd19 100644 (file)
@@ -2767,12 +2767,13 @@ function wfEscapeShellArg( ) {
  *                 (non-zero is usually failure)
  * @param $environ Array optional environment variables which should be
  *                 added to the executed command environment.
- * @param $limits Array optional array with limits(filesize, memory, time)
+ * @param $limits Array optional array with limits(filesize, memory, time, walltime)
  *                 this overwrites the global wgShellMax* limits.
  * @return string collected stdout as a string (trailing newlines stripped)
  */
 function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array() ) {
-       global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime;
+       global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime,
+               $wgMaxShellWallClockTime;
 
        static $disabled;
        if ( is_null( $disabled ) ) {
@@ -2820,14 +2821,19 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array
 
        if ( php_uname( 's' ) == 'Linux' ) {
                $time = intval ( isset($limits['time']) ? $limits['time'] : $wgMaxShellTime );
+               if ( isset( $limits['walltime'] ) ) {
+                       $wallTime = intval( $limits['walltime'] );
+               } elseif ( isset( $limits['time'] ) ) {
+                       $wallTime = $time;
+               } else {
+                       $wallTime = intval( $wgMaxShellWallClockTime );
+               }
                $mem = intval ( isset($limits['memory']) ? $limits['memory'] : $wgMaxShellMemory );
                $filesize = intval ( isset($limits['filesize']) ? $limits['filesize'] : $wgMaxShellFileSize );
 
-               if ( $time > 0 && $mem > 0 ) {
-                       $script = "$IP/bin/ulimit4.sh";
-                       if ( is_executable( $script ) ) {
-                               $cmd = '/bin/bash ' . escapeshellarg( $script ) . " $time $mem $filesize " . escapeshellarg( $cmd );
-                       }
+               if ( $time > 0 || $mem > 0 || $filesize > 0 || $wallTime > 0 ) {
+                       $cmd = '/bin/bash ' . escapeshellarg( "$IP/bin/ulimit5.sh" ) .
+                               " $time $mem $filesize $wallTime " . escapeshellarg( $cmd );
                }
        }
        wfDebug( "wfShellExec: $cmd\n" );