X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;ds=sidebyside;f=includes%2FGlobalFunctions.php;h=11388e8607f9bbd9ce6905dbe5dfebfd2d60a56f;hb=17cc78767ab099f9d00d267f230be81c202b1795;hp=ddea620b815dcb637bcd2686bd2e0a8f037437a3;hpb=db6b2cb3c252479ed6e9cf80100c3802528bd45f;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index ddea620b81..11388e8607 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -102,19 +102,30 @@ if ( !function_exists( 'gzdecode' ) ) { } // hash_equals function only exists in PHP >= 5.6.0 +// http://php.net/hash_equals if ( !function_exists( 'hash_equals' ) ) { /** - * Check whether a user-provided string is equal to a fixed-length secret without - * revealing bytes of the secret through timing differences. + * Check whether a user-provided string is equal to a fixed-length secret string + * without revealing bytes of the secret string through timing differences. * - * This timing guarantee -- that a partial match takes the same time as a complete - * mismatch -- is why this function is used in some security-sensitive parts of the code. - * For example, it shouldn't be possible to guess an HMAC signature one byte at a time. + * The usual way to compare strings (PHP's === operator or the underlying memcmp() + * function in C) is to compare corresponding bytes and stop at the first difference, + * which would take longer for a partial match than for a complete mismatch. This + * is not secure when one of the strings (e.g. an HMAC or token) must remain secret + * and the other may come from an attacker. Statistical analysis of timing measurements + * over many requests may allow the attacker to guess the string's bytes one at a time + * (and check his guesses) even if the timing differences are extremely small. + * + * When making such a security-sensitive comparison, it is essential that the sequence + * in which instructions are executed and memory locations are accessed not depend on + * the secret string's value. HOWEVER, for simplicity, we do not attempt to minimize + * the inevitable leakage of the string's length. That is generally known anyway as + * a chararacteristic of the hash function used to compute the secret value. * * Longer explanation: http://www.emerose.com/timing-attacks-explained * * @codeCoverageIgnore - * @param string $known_string Fixed-length secret to compare against + * @param string $known_string Fixed-length secret string to compare against * @param string $user_string User-provided string * @return bool True if the strings are the same, false otherwise */ @@ -134,14 +145,14 @@ if ( !function_exists( 'hash_equals' ) ) { return false; } - // Note that we do one thing PHP doesn't: try to avoid leaking information about - // relative lengths of $known_string and $user_string, and of multiple $known_strings. - // However, lengths may still inevitably leak through, for example, CPU cache misses. $known_string_len = strlen( $known_string ); - $user_string_len = strlen( $user_string ); - $result = $known_string_len ^ $user_string_len; - for ( $i = 0; $i < $user_string_len; $i++ ) { - $result |= ord( $known_string[$i % $known_string_len] ) ^ ord( $user_string[$i] ); + if ( $known_string_len !== strlen( $user_string ) ) { + return false; + } + + $result = 0; + for ( $i = 0; $i < $known_string_len; $i++ ) { + $result |= ord( $known_string[$i] ) ^ ord( $user_string[$i] ); } return ( $result === 0 ); @@ -1407,7 +1418,7 @@ function wfGetLangObj( $langcode = false ) { * * This function replaces all old wfMsg* functions. * - * @param string $key Message key + * @param string|string[] $key Message key, or array of keys * @param mixed $params,... Normal message parameters * @return Message * @@ -2975,7 +2986,9 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), * function, as all the arguments to wfShellExec can become unwieldy. * * @note This also includes errors from limit.sh, e.g. if $wgMaxShellFileSize is exceeded. - * @param string $cmd Command line, properly escaped for shell. + * @param string|string[] $cmd If string, a properly shell-escaped command line, + * or an array of unescaped arguments, in which case each value will be escaped + * Example: [ 'convert', '-font', 'font name' ] would produce "'convert' '-font' 'font name'" * @param null|mixed &$retval Optional, will receive the program's exit code. * (non-zero is usually failure) * @param array $environ Optional environment variables which should be @@ -3179,10 +3192,10 @@ function wfDiff( $before, $after, $params = '-u' ) { // Kill the --- and +++ lines. They're not useful. $diff_lines = explode( "\n", $diff ); - if ( strpos( $diff_lines[0], '---' ) === 0 ) { + if ( isset( $diff_lines[0] ) && strpos( $diff_lines[0], '---' ) === 0 ) { unset( $diff_lines[0] ); } - if ( strpos( $diff_lines[1], '+++' ) === 0 ) { + if ( isset( $diff_lines[1] ) && strpos( $diff_lines[1], '+++' ) === 0 ) { unset( $diff_lines[1] ); } @@ -3366,7 +3379,10 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, ); if ( extension_loaded( 'gmp' ) && ( $engine == 'auto' || $engine == 'gmp' ) ) { - $result = gmp_strval( gmp_init( $input, $sourceBase ), $destBase ); + // Removing leading zeros works around broken base detection code in + // some PHP versions (see and + // ). + $result = gmp_strval( gmp_init( ltrim( $input, '0' ), $sourceBase ), $destBase ); } elseif ( extension_loaded( 'bcmath' ) && ( $engine == 'auto' || $engine == 'bcmath' ) ) { $decimal = '0'; foreach ( str_split( strtolower( $input ) ) as $char ) { @@ -3749,46 +3765,77 @@ function wfGetNull() { } /** - * Modern version of wfWaitForSlaves(). Instead of looking at replication lag - * and waiting for it to go down, this waits for the slaves to catch up to the - * master position. Use this when updating very large numbers of rows, as - * in maintenance scripts, to avoid causing too much lag. Of course, this is - * a no-op if there are no slaves. + * Waits for the slaves to catch up to the master position + * + * Use this when updating very large numbers of rows, as in maintenance scripts, + * to avoid causing too much lag. Of course, this is a no-op if there are no slaves. + * + * By default this waits on the main DB cluster of the current wiki. + * If $cluster is set to "*" it will wait on all DB clusters, including + * external ones. If the lag being waiting on is caused by the code that + * does this check, it makes since to use $ifWritesSince, particularly if + * cluster is "*", to avoid excess overhead. + * + * Never call this function after a big DB write that is still in a transaction. + * This only makes sense after the possible lag inducing changes were committed. * * @param float|null $ifWritesSince Only wait if writes were done since this UNIX timestamp * @param string|bool $wiki Wiki identifier accepted by wfGetLB * @param string|bool $cluster Cluster name accepted by LBFactory. Default: false. + * @param int|null $timeout Max wait time. Default: 1 day (cli), ~10 seconds (web) * @return bool Success (able to connect and no timeouts reached) */ -function wfWaitForSlaves( $ifWritesSince = false, $wiki = false, $cluster = false ) { +function wfWaitForSlaves( + $ifWritesSince = null, $wiki = false, $cluster = false, $timeout = null +) { // B/C: first argument used to be "max seconds of lag"; ignore such values - $ifWritesSince = ( $ifWritesSince > 1e9 ) ? $ifWritesSince : false; + $ifWritesSince = ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null; - if ( $cluster !== false ) { - $lb = wfGetLBFactory()->getExternalLB( $cluster ); - } else { - $lb = wfGetLB( $wiki ); + if ( $timeout === null ) { + $timeout = ( PHP_SAPI === 'cli' ) ? 86400 : 10; } - // bug 27975 - Don't try to wait for slaves if there are none - // Prevents permission error when getting master position - if ( $lb->getServerCount() > 1 ) { - if ( $ifWritesSince && !$lb->hasMasterConnection() ) { - return true; // assume no writes done - } - $dbw = $lb->getConnection( DB_MASTER, array(), $wiki ); - if ( $ifWritesSince && $dbw->lastDoneWrites() < $ifWritesSince ) { - return true; // no writes since the last wait + // Figure out which clusters need to be checked + $lbs = array(); + if ( $cluster === '*' ) { + wfGetLBFactory()->forEachLB( function( LoadBalancer $lb ) use ( &$lbs ) { + $lbs[] = $lb; + } ); + } elseif ( $cluster !== false ) { + $lbs[] = wfGetLBFactory()->getExternalLB( $cluster ); + } else { + $lbs[] = wfGetLB( $wiki ); + } + + // Get all the master positions of applicable DBs right now. + // This can be faster since waiting on one cluster reduces the + // time needed to wait on the next clusters. + $masterPositions = array_fill( 0, count( $lbs ), false ); + foreach ( $lbs as $i => $lb ) { + // bug 27975 - Don't try to wait for slaves if there are none + // Prevents permission error when getting master position + if ( $lb->getServerCount() > 1 ) { + if ( $ifWritesSince && !$lb->hasMasterConnection() ) { + continue; // assume no writes done + } + $dbw = $lb->getConnection( DB_MASTER, array(), $wiki ); + if ( $ifWritesSince && $dbw->lastDoneWrites() < $ifWritesSince ) { + continue; // no writes since the last wait + } + $masterPositions[$i] = $dbw->getMasterPos(); } - $pos = $dbw->getMasterPos(); - // The DBMS may not support getMasterPos() or the whole - // load balancer might be fake (e.g. $wgAllDBsAreLocalhost). - if ( $pos !== false ) { - return $lb->waitForAll( $pos, PHP_SAPI === 'cli' ? 86400 : null ); + } + + $ok = true; + foreach ( $lbs as $i => $lb ) { + if ( $masterPositions[$i] ) { + // The DBMS may not support getMasterPos() or the whole + // load balancer might be fake (e.g. $wgAllDBsAreLocalhost). + $ok = $lb->waitForAll( $masterPositions[$i], $timeout ) && $ok; } } - return true; + return $ok; } /**