Page MenuHomePhabricator

Undefined offset exception in "PhutilProseDifferenceEngine::stitchPieces()"
Closed, ResolvedPublic

Description

See PHI1821. Likely after D21044, prose diffs may attempt to access an undefined index when stitching corpus components together. This may be similar to D21295.

Stack trace:

[2020-07-22 10:34:10] EXCEPTION: (PhutilProxyException) Error while executing Task ID 1618768. {>} (RuntimeException) Undefined offset: 1 at [<arcanist>/src/error/PhutilErrorHandler.php:258]
arcanist(head=stable, ref.master=3b6b523c2b23, ref.stable=2565cc7b4d1d), libcore(), phabricator(head=stable, ref.stable=2710a1e7aa42), services(head=stable, ref.master=772620edd80c, ref.stable=911b948c8e1f)
  #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer, array) called at [<phabricator>/src/infrastructure/diff/prose/PhutilProseDifferenceEngine.php:153]
  #1 <#2> PhutilProseDifferenceEngine::stitchPieces(array, integer) called at [<phabricator>/src/infrastructure/diff/prose/PhutilProseDifferenceEngine.php:132]
  #2 <#2> PhutilProseDifferenceEngine::splitCorpus(string, integer) called at [<phabricator>/src/infrastructure/diff/prose/PhutilProseDifferenceEngine.php:10]
  #3 <#2> PhutilProseDifferenceEngine::buildDiff(string, string, integer) called at [<phabricator>/src/infrastructure/diff/prose/PhutilProseDifferenceEngine.php:6]
  #4 <#2> PhutilProseDifferenceEngine::getDiff(string, string) called at [<phabricator>/src/applications/transactions/view/PhabricatorApplicationTransactionDetailView.php:170]
  #5 <#2> PhabricatorApplicationTransactionDetailView::buildDiff() called at [<phabricator>/src/applications/transactions/view/PhabricatorApplicationTransactionDetailView.php:19]
  #6 <#2> PhabricatorApplicationTransactionDetailView::renderForMail() called at [<phabricator>/src/applications/transactions/storage/PhabricatorApplicationTransaction.php:1460]
  #7 <#2> PhabricatorApplicationTransaction::renderChangeDetailsForMail(PhabricatorUser) called at [<phabricator>/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php:3629]
  #8 <#2> PhabricatorApplicationTransactionEditor::addHeadersAndCommentsToMailBody(PhabricatorMetaMTAMailBody, array, string, string) called at [<phabricator>/src/applications/differential/editor/DifferentialTransactionEditor.php:632]
  #9 <#2> DifferentialTransactionEditor::buildMailBody(DifferentialRevision, array) called at [<phabricator>/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php:3166]
  #10 <#2> PhabricatorApplicationTransactionEditor::buildMailForTarget(DifferentialRevision, array, PhabricatorMailTarget) called at [<phabricator>/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php:3109]
  #11 <#2> PhabricatorApplicationTransactionEditor::buildMailWithRecipients(DifferentialRevision, array, array, array, array) called at [<phabricator>/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php:3052]
  #12 <#2> PhabricatorApplicationTransactionEditor::buildMail(DifferentialRevision, array) called at [<phabricator>/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php:1494]
  #13 <#2> PhabricatorApplicationTransactionEditor::publishTransactions(DifferentialRevision, array) called at [<phabricator>/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php:21]
  #14 <#2> PhabricatorApplicationTransactionPublishWorker::doWork() called at [<phabricator>/src/infrastructure/daemon/workers/PhabricatorWorker.php:124]
  #15 <#2> PhabricatorWorker::executeTask() called at [<phabricator>/src/infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php:159]
  #16 <#2> PhabricatorWorkerActiveTask::executeTask() called at [<phabricator>/src/infrastructure/daemon/workers/PhabricatorTaskmasterDaemon.php:22]
  #17 PhabricatorTaskmasterDaemon::run() called at [<phabricator>/src/infrastructure/daemon/PhutilDaemon.php:219]
  #18 PhutilDaemon::execute() called at [<phabricator>/scripts/daemon/exec/exec_daemon.php:131]

Event Timeline

epriestley created this task.

This is a PCRE backtracking issue. It reproduces on an input corpus of length 3,277 bytes with only "x", " " (space), and "\n" (newline) characters, and disappears if I increase pcre.backtrack_limit.

The use of the ? in the second capturing expression means that we must backtrack for all spaces in the expression.

This test case reproduces it, depending on the value of pcre.backtrack_limit:

$this->assertProseParts(
  str_repeat('x ', 1024 * 1024),
  str_repeat('x ', 1024 * 1024),
  array(),
  pht('???'));

I think a single regular expression is not actually the best tool here.