From 854a2b8bccfda2df349cdf868ac5fe61f21df2a7 Mon Sep 17 00:00:00 2001
From: Max Semenik <maxsem.wiki@gmail.com>
Date: Tue, 12 Apr 2016 16:52:34 -0400
Subject: [PATCH] Use pool counter for generating large diffs to prevent DoS.

Bug: T130947
Change-Id: If560844664051c04e01b954377b4bdfdb744d13f
---
 includes/diff/DifferenceEngine.php | 35 ++++++++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/includes/diff/DifferenceEngine.php b/includes/diff/DifferenceEngine.php
index 1508cf1..741fc20 100644
--- a/includes/diff/DifferenceEngine.php
+++ b/includes/diff/DifferenceEngine.php
@@ -842,19 +842,36 @@ class DifferenceEngine extends ContextSource {
 	 * @return bool|string
 	 */
 	public function generateTextDiffBody( $otext, $ntext ) {
-		$time = microtime( true );
+		$diff = function() use ( $otext, $ntext ) {
+			$time = microtime( true );
 
-		$result = $this->textDiff( $otext, $ntext );
+			$result = $this->textDiff( $otext, $ntext );
 
-		$time = intval( ( microtime( true ) - $time ) * 1000 );
-		$this->getStats()->timing( 'diff_time', $time );
-		// Log requests slower than 99th percentile
-		if ( $time > 100 && $this->mOldPage && $this->mNewPage ) {
-			wfDebugLog( 'diff',
-				"$time ms diff: {$this->mOldid} -> {$this->mNewid} {$this->mNewPage}" );
+			$time = intval( ( microtime( true ) - $time ) * 1000 );
+			$this->getStats()->timing( 'diff_time', $time );
+			// Log requests slower than 99th percentile
+			if ( $time > 100 && $this->mOldPage && $this->mNewPage ) {
+				wfDebugLog( 'diff',
+					"$time ms diff: {$this->mOldid} -> {$this->mNewid} {$this->mNewPage}" );
+			}
+
+			return $result;
+		};
+
+		$error = function( $status ) {
+			throw new FatalError( $status->getWikiText() );
+		};
+
+		// Use PoolCounter if the diff looks like it can be expensive
+		if ( strlen( $otext ) + strlen( $ntext ) > 20000 ) {
+			$work = new PoolCounterWorkViaCallback( 'diff',
+				md5( $otext ) . md5( $ntext ),
+				[ 'doWork' => $diff, 'error' => $error ]
+			);
+			return $work->execute();
 		}
 
-		return $result;
+		return $diff();
 	}
 
 	/**
-- 
2.0.1

