Skip to content

Internal Error on MW 1.43 with $wgUseInstantCommons enabled #16

@Nydauron

Description

@Nydauron

Problem

I have a wiki running on 1.35 with $wgUseInstantCommons = true, so that Wiki Commons' assets can be referenced (e.g. State flag SVGs and more). I recently attempted an upgrade to 1.43, and while everything else looked fine, whenever visiting a page that referenced a Commons asset, the page would return back with an internal error message with the following backtrace:

[848e99a21dff03dbf92ee905] /wiki/Main_Page Error: Call to undefined method GuzzleHttp\Utils::chooseHandler()
Backtrace:
from /var/www/html/w/vendor/guzzlehttp/guzzle/src/functions.php(61)
#0 /var/www/html/forums/vendor/guzzlehttp/guzzle/src/HandlerStack.php(42): GuzzleHttp\choose_handler()
#1 /var/www/html/w/includes/http/GuzzleHttpRequest.php(164): GuzzleHttp\HandlerStack::create()
#2 /var/www/html/w/includes/filerepo/ForeignAPIRepo.php(555): GuzzleHttpRequest->execute()
#3 /var/www/html/w/includes/filerepo/ForeignAPIRepo.php(602): ForeignAPIRepo::httpGet()
#4 /var/www/html/w/includes/libs/objectcache/WANObjectCache.php(1808): ForeignAPIRepo->{closure}()
#5 /var/www/html/w/includes/libs/objectcache/WANObjectCache.php(1623): Wikimedia\ObjectCache\WANObjectCache-
>fetchOrRegenerate()
#6 /var/www/html/w/includes/filerepo/ForeignAPIRepo.php(595): Wikimedia\ObjectCache\WANObjectCache-
>getWithSetCallback()
#7 /var/www/html/w/includes/filerepo/ForeignAPIRepo.php(221): ForeignAPIRepo->httpGetCached()
#8 /var/www/html/w/includes/filerepo/file/ForeignAPIFile.php(62): ForeignAPIRepo->fetchImageQuery()
#9 /var/www/html/w/includes/filerepo/FileRepo.php(439): ForeignAPIFile::newFromTitle()
#10 /var/www/html/w/includes/filerepo/ForeignAPIRepo.php(134): FileRepo->newFile()
#11 /var/www/html/w/includes/filerepo/FileRepo.php(480): ForeignAPIRepo->newFile()
#12 /var/www/html/w/includes/filerepo/RepoGroup.php(147): FileRepo->findFile()
#13 /var/www/html/w/includes/parser/Parser.php(3825): RepoGroup->findFile()
#14 /var/www/html/w/includes/parser/Parser.php(3791): MediaWiki\Parser\Parser->fetchFileNoRegister()
#15 /var/www/html/w/includes/parser/Parser.php(5399): MediaWiki\Parser\Parser->fetchFileAndTitle()
#16 /var/www/html/w/includes/parser/Parser.php(2715): MediaWiki\Parser\Parser->makeImage()
#17 /var/www/html/w/includes/parser/Parser.php(2462): MediaWiki\Parser\Parser->handleInternalLinks2()
#18 /var/www/html/w/includes/parser/Parser.php(1627): MediaWiki\Parser\Parser->handleInternalLinks()
#19 /var/www/html/w/includes/parser/Parser.php(701): MediaWiki\Parser\Parser->internalParse()
#20 /var/www/html/w/includes/content/WikitextContentHandler.php(384): MediaWiki\Parser\Parser->parse()
#21 /var/www/html/w/includes/content/ContentHandler.php(1691): MediaWiki\Content\WikitextContentHandler-
>fillParserOutput()
#22 /var/www/html/w/includes/content/Renderer/ContentRenderer.php(79): MediaWiki\Content\ContentHandler-
>getParserOutput()
#23 /var/www/html/w/includes/Revision/RenderedRevision.php(263): MediaWiki\Content\Renderer\ContentRenderer-
>getParserOutput()
#24 /var/www/html/w/includes/Revision/RenderedRevision.php(236): MediaWiki\Revision\RenderedRevision-
>getSlotParserOutputUncached()
#25 /var/www/html/w/includes/Revision/RevisionRenderer.php(239): MediaWiki\Revision\RenderedRevision-
>getSlotParserOutput()
#26 /var/www/html/w/includes/Revision/RevisionRenderer.php(172): MediaWiki\Revision\RevisionRenderer-
>combineSlotOutput()
#27 [internal function]: MediaWiki\Revision\RevisionRenderer->MediaWiki\Revision\{closure}()
#28 /var/www/html/w/includes/Revision/RenderedRevision.php(199): call_user_func()
#29 /var/www/html/w/includes/poolcounter/PoolWorkArticleView.php(106): MediaWiki\Revision\RenderedRevision-
>getRevisionParserOutput()
#30 /var/www/html/w/includes/poolcounter/PoolWorkArticleViewCurrent.php(123):
MediaWiki\PoolCounter\PoolWorkArticleView->renderRevision()
#31 /var/www/html/w/includes/poolcounter/PoolCounterWork.php(171): MediaWiki\PoolCounter\PoolWorkArticleViewCurrent-
>doWork()
#32 /var/www/html/w/includes/page/ParserOutputAccess.php(362): MediaWiki\PoolCounter\PoolCounterWork->execute()
#33 /var/www/html/w/includes/page/Article.php(827): MediaWiki\Page\ParserOutputAccess->getParserOutput()
#34 /var/www/html/w/includes/page/Article.php(547): Article->generateContentOutput()
#35 /var/www/html/w/includes/actions/ViewAction.php(78): Article->view()
#36 /var/www/html/w/includes/actions/ActionEntryPoint.php(733): ViewAction->show()
#37 /var/www/html/w/includes/actions/ActionEntryPoint.php(510): MediaWiki\Actions\ActionEntryPoint->performAction()
#38 /var/www/html/w/includes/actions/ActionEntryPoint.php(146): MediaWiki\Actions\ActionEntryPoint->performRequest()
#39 /var/www/html/w/includes/MediaWikiEntryPoint.php(200): MediaWiki\Actions\ActionEntryPoint->execute()
#40 /var/www/html/w/index.php(58): MediaWiki\MediaWikiEntryPoint->run()
#41 {main}

Basic Repro Steps

  1. Use PHPBB 3.3.14 and MW 1.43 (MW 1.39 should work as well) with the following minimal LocalSettings.php config below. Please read the comments to see what to change:
<?php
if ( !defined( 'MEDIAWIKI' ) ) {
        exit;
}

$wgSitename = "My Wiki";

$wgScriptExtension  = ".php";
$wgScriptPath = "/w";
$wgScript = "/w/index.php";
$wgArticlePath = "/wiki/$1";
$wgUsePathInfo = true;

$wgServer = "http://192.168.42.149"; // Change to be your server host
$wgStylePath        = "$wgScriptPath/skins";
$wgLogo             = "$wgScriptPath/resources/assets/wiki.png";

// This just contains `$database_password`, which is an array of username to password mappings
require("/nonweb/database-passwords.php");

$wgDBtype           = "mysql";
$wgDBserver         = "localhost";
$wgDBname           = "wiki";
$wgDBuser           = "wiki";
$wgDBpassword       = $database_password[$wgDBuser];

$wgDBprefix         = "wiki_";

$wgDBTableOptions   = "ENGINE=InnoDB, DEFAULT CHARSET=binary";
$wgDBmysql5 = false;

/*
 * If you do load Phpbbauth, then turning this to false will prevent the error, but 
 * loading a page that requires WikiCommons resources will result in no image.
 */
$wgUseInstantCommons  = true;

wfLoadSkin('Vector');
$wgDefaultSkin="vector-2022";

$wgShowExceptionDetails = true;

wfLoadExtension( 'Auth_remoteuser' );
$wgGroupPermissions['*']['createaccount'] = false;
$wgGroupPermissions['*']['autocreateaccount'] = true;
/*
 * Uncommenting the two commented lines to load the extension causes the error.
 * In step 2, you will uncomment those two lines to produce the error.
 */
// wfLoadExtension( 'Phpbbauth' );
$wgPhpbbAuthForumDirectory = '/var/www/html/forums/';
$wgPhpbbAuthAbsolutePath = '/forums/';
$wgPhpbbAuthNameFormat = 'phpbb';
// require_once "$IP/extensions/Phpbbauth/PhpbbAuth.php";
wfLoadExtension( 'ParserFunctions' );

Use Auth_remoteuser 2.1.1(c985d52) (use respective version for 1.39), which should be the version that the ExtensionDistributor should give for REL1_43. For phpbbauth, use master. Verify that everything works so far by going to Special:Version.

  1. Ensure that Commons' assets are loading on the newly installed Wiki (e.g. [[File:State of California.svg]]), and make a page referencing them. You can also make another page without any Commons' references (e.g. Special:Version) to test the difference. You can check the file page for these Commons' assets and there should be some text stating: This file is from Wikimedia Commons and may be used by other projects.
  2. Enable Phpbbauth by loading it inside your LocalSettings.php. You can do so by uncommenting the wfLoadExtension and require_once lines. If you revisit and refresh the pages that reference the Commons' asset, then the page results in the error message above. You can go and visit pages that don't reference Commons' assets and they should be able to load fine.

The Likely Cause

Looking at the backtrace, we can see the following line:

#0 /var/www/html/forums/vendor/guzzlehttp/guzzle/src/HandlerStack.php(42): GuzzleHttp\choose_handler()
#1 /var/www/html/w/includes/http/GuzzleHttpRequest.php(164): GuzzleHttp\HandlerStack::create()

which shows MW is calling a dependency within PHPBB.

Looking into the dependencies of phpBB and MW:

  • phpBB 3.3.x requires guzzlehttp/guzzle: ~6.3
  • MW 1.35 strictly uses guzzlehttp/guzzle: 6.5.8 which fits ~6.3 and looking at phpBB's lock file, guzzle is pinned to 6.5.8 in version 3.3.14.
  • MW 1.39 strictly uses guzzlehttp/guzzle: 7.4.5
  • MW 1.43 strictly uses guzzlehttp/guzzle: 7.9.2

So, looking at this, it explains why MW works on 1.35, but not 1.43, and presumably also 1.39.

I moved /forums to trigger an error to figure out who was first initializing phpBB, and it led to:

require $phpbb_root_path . 'common.' . $phpEx;

And looking into phpBB's /common.php, we can see:
https://github.com/phpbb/phpbb/blob/72ae28d583dcb248fdd3d672378bfc32515facc5/phpBB/common.php#L23-L24
https://github.com/phpbb/phpbb/blob/72ae28d583dcb248fdd3d672378bfc32515facc5/phpBB/includes/startup.php#L66-L84

Basically a case where two autoloaders are being run. but have dependencies of different versions which is UB:
composer/composer#12087 (comment)

Some potential solutions

  • Set env var PHPBB_NO_COMPOSER_AUTOLOAD=1 before calling common.php

There is some preamble about autoloading in phpBB within includes/startup.php:

// Autoloading of dependencies.
// Three options are supported:
// 1. If dependencies are installed with Composer, Composer will create a
//    vendor/autoload.php. If this file exists it will be
//    automatically used by phpBB. This is the default mode that phpBB
//    will use when shipped.
// 2. To disable composer autoloading, PHPBB_NO_COMPOSER_AUTOLOAD can be specified.
// 	  Additionally specify PHPBB_AUTOLOAD=/path/to/autoload.php in the
//    environment. This is useful for running CLI scripts and tests.
//    /path/to/autoload.php should define and register class loaders
//    for all of phpBB's dependencies.
// 3. You can also set PHPBB_NO_COMPOSER_AUTOLOAD without setting PHPBB_AUTOLOAD.
//    In this case autoloading needs to be defined before running any phpBB
//    script. This might be useful in cases when phpBB is integrated into a
//    larger program.

Might be a good place to start, but it might not be the only change that gets made.

  • Handle session management manually

It is technically possible to simply handle session management manually since we should have access to phpBB's database. The big drawback that this comes with a lot of security implcations because simply checking if the session id exists and has a non-bot user id in the sessions table is simply not good enough. sid would then become a vulnerable secret if it were to be leaked without the IP, user-agent, and referrer checks, and looking at the User::session_begin() functions show a lot of business logic to simply validate a session.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions