Page MenuHomePhabricator

ApiGlobalBlock.php: Trying to get property 'gb_expiry' or 'gb_anon_only ' of non-object
Closed, ResolvedPublicPRODUCTION ERROR

Description

Notice: Cannot access property on non-object in /srv/mediawiki/php-1.31.0-wmf.6/extensions/GlobalBlocking/includes/api/ApiGlobalBlock.php on line 53

Notice: Cannot access property on non-object in /srv/mediawiki/php-1.31.0-wmf.6/extensions/GlobalBlocking/includes/api/ApiGlobalBlock.php on line 56

Notice: Cannot access property on non-object in /srv/mediawiki/php-1.31.0-wmf.6/extensions/GlobalBlocking/includes/api/ApiGlobalBlock.php on line 59

Currently trending on Fatal-Monitor dashboard in Logstash.

Looks like at least one of these is due to GlobalBlocking::getGlobalBlockingBlock() not consistently returning an object with the same shape. Not whether due to a logic error in PHP, or a Database select/schema issue.

Event Timeline

GlobalBlocking::getGlobalBlockingBlock() does SELECT *... So, I'm guessing it's returning false

Only in one case does it actually check if $block evaluates to true as PHP reckons... It probably should check in more points

			if ( $block && $this->getParameter( 'modify' ) ) {
				$options[] = 'modify';
			}

Not seen in the last 7 days.

Krinkle moved this task from Resolved to Older on the Wikimedia-production-error board.
Krinkle added a subscriber: Legoktm.

Looks like it was only absent for a little while due to relating in some way to what kind of blocks admins on wikis have created and then subsequently request information about. This API is only rarely used.

Seen in Logstash since at least 1.34-wmf.3.

Latest sample:

[{exception_id}] {exception_url} ErrorException from line 46 of /srv/mediawiki/php-1.34.0-wmf.7/extensions/GlobalBlocking/includes/api/ApiGlobalBlock.php: PHP Notice: Cannot access property on non-object

[{exception_id}] {exception_url} ErrorException from line 49 of /srv/mediawiki/php-1.34.0-wmf.7/extensions/GlobalBlocking/includes/api/ApiGlobalBlock.php: PHP Notice: Cannot access property on non-object

[{exception_id}] {exception_url} ErrorException from line 52 of /srv/mediawiki/php-1.34.0-wmf.6/extensions/GlobalBlocking/includes/api/ApiGlobalBlock.php: PHP Notice: Cannot access property on non-object
  • Request ID: XPLl6gpAIDgAAE5gB7AAAABL
  • Requet URL: /w/api.php?action=globalblock&format=json
#0 /srv/mediawiki/php-1.34.0-wmf.7/extensions/GlobalBlocking/includes/api/ApiGlobalBlock.php(46): MWExceptionHandler::handleError(integer, string, string, integer, array, array)
#1 /srv/mediawiki/php-1.34.0-wmf.7/includes/api/ApiMain.php(1595): ApiGlobalBlock->execute()
#2 /srv/mediawiki/php-1.34.0-wmf.7/includes/api/ApiMain.php(531): ApiMain->executeAction()
#3 /srv/mediawiki/php-1.34.0-wmf.7/includes/api/ApiMain.php(502): ApiMain->executeActionWithErrorHandling()
#4 /srv/mediawiki/php-1.34.0-wmf.7/api.php(87): ApiMain->execute()
#5 /srv/mediawiki/w/api.php(3): include(string)
#6 {main}
Krinkle renamed this task from Fix "PHP Notice: Cannot access property on non-object in extensions/GlobalBlocking/includes/api/ApiGlobalBlock.php" to ApiGlobalBlock.php: Trying to get property 'gb_expiry' or 'gb_anon_only ' of non-object.Jul 24 2019, 5:59 PM

Request URL: meta.wikimedia.org /w/api.php
Request ID: XTiAGQpAIDwAAIe1-vMAAAAJ

message
[{exception_id}] {exception_url}   ErrorException from line 52 of /srv/mediawiki/php-1.34.0-wmf.15/extensions/GlobalBlocking/includes/api/ApiGlobalBlock.php: PHP Notice: Trying to get property 'gb_expiry' of non-object
trace
#0 /srv/mediawiki/php-1.34.0-wmf.15/extensions/GlobalBlocking/includes/api/ApiGlobalBlock.php(52): MWExceptionHandler::handleError(integer, string, string, integer, array)
#1 /srv/mediawiki/php-1.34.0-wmf.15/includes/api/ApiMain.php(1583): ApiGlobalBlock->execute()
#2 /srv/mediawiki/php-1.34.0-wmf.15/includes/api/ApiMain.php(531): ApiMain->executeAction()
#3 /srv/mediawiki/php-1.34.0-wmf.15/includes/api/ApiMain.php(502): ApiMain->executeActionWithErrorHandling()
#4 /srv/mediawiki/php-1.34.0-wmf.15/api.php(86): ApiMain->execute()
#5 /srv/mediawiki/w/api.php(3): require(string)
#6 {main}

Same request id also has

PHP Notice: Trying to get property 'gb_anon_only' of non-object

at

#0 /srv/mediawiki/php-1.34.0-wmf.15/extensions/GlobalBlocking/includes/api/ApiGlobalBlock.php(46): MWExceptionHandler::handleError(integer, string, string, integer, array)
#1 /srv/mediawiki/php-1.34.0-wmf.15/includes/api/ApiMain.php(1583): ApiGlobalBlock->execute()
#2 /srv/mediawiki/php-1.34.0-wmf.15/includes/api/ApiMain.php(531): ApiMain->executeAction()
#3 /srv/mediawiki/php-1.34.0-wmf.15/includes/api/ApiMain.php(502): ApiMain->executeActionWithErrorHandling()
#4 /srv/mediawiki/php-1.34.0-wmf.15/api.php(86): ApiMain->execute()
#5 /srv/mediawiki/w/api.php(3): require(string)
#6 {main}

I can reproduce this by creating a GlobalBlock of an IP range via the API.

I see in the logs:

[DBQuery] GlobalBlocking::getGlobalBlockingBlock [0s]: SELECT  gb_id,gb_address,gb_by,gb_by_wiki,gb_reason,gb_timestamp,gb_anon_only,gb_expiry,gb_range_start,gb_range_end  FROM `globalblocks`    WHERE (gb_range_start  LIKE '%' ESCAPE '`' ) AND (gb_range_start <= '0') AND (gb_range_end >= '0') AND (gb_expiry > '20190724174208')  LIMIT 1

I do not believe GlobalBlocking::getRangeCondition (which is called by GlobalBlocking::getGlobalBlockingBlock) correctly interprets IP ranges.

I do not believe GlobalBlocking::getRangeCondition (which is called by GlobalBlocking::getGlobalBlockingBlock) correctly interprets IP ranges.

reedy@deploy1001:~$ mwscript eval.php enwiki
> $dbr = wfGetDB( DB_REPLICA );

> $hexIp = IP::toHex( '127.0.0.1' );

> $ipPattern = substr( $hexIp, 0, 4 );

> $quotedHex = $dbr->addQuotes( $hexIp );

> $cond = [ 'gb_range_start ' . $dbr->buildLike( $ipPattern, $dbr->anyString() ), 'gb_range_start <= ' . $quotedHex, 'gb_range_end >= ' . $quotedHex, 'gb_expiry > ' . $dbr->addQuotes( $dbr->timestamp( wfTimestampNow() ) ) ];

> var_dump( $cond );
array(4) {
  [0]=>
  string(40) "gb_range_start  LIKE '7F00%' ESCAPE '`' "
  [1]=>
  string(28) "gb_range_start <= '7F000001'"
  [2]=>
  string(26) "gb_range_end >= '7F000001'"
  [3]=>
  string(28) "gb_expiry > '20190726163645'"
}

> $hexIp = IP::toHex( '10.0.0.0/16' );

> $ipPattern = substr( $hexIp, 0, 4 );

> $quotedHex = $dbr->addQuotes( $hexIp );

> $cond = [ 'gb_range_start ' . $dbr->buildLike( $ipPattern, $dbr->anyString() ), 'gb_range_start <= ' . $quotedHex, 'gb_range_end >= ' . $quotedHex, 'gb_expiry > ' . $dbr->addQuotes( $dbr->timestamp( wfTimestampNow() ) ) ];

> var_dump( $cond );
array(4) {
  [0]=>
  string(36) "gb_range_start  LIKE '%' ESCAPE '`' "
  [1]=>
  string(21) "gb_range_start <= '0'"
  [2]=>
  string(19) "gb_range_end >= '0'"
  [3]=>
  string(28) "gb_expiry > '20190726164630'"
}

> 

> $hexIp = IP::toHex( '10.0.0.0-255' );

> $ipPattern = substr( $hexIp, 0, 4 );

> $quotedHex = $dbr->addQuotes( $hexIp );

> $cond = [ 'gb_range_start ' . $dbr->buildLike( $ipPattern, $dbr->anyString() ), 'gb_range_start <= ' . $quotedHex, 'gb_range_end >= ' . $quotedHex, 'gb_expiry > ' . $dbr->addQuotes( $dbr->timestamp( wfTimestampNow() ) ) ];

> var_dump( $cond );
array(4) {
  [0]=>
  string(36) "gb_range_start  LIKE '%' ESCAPE '`' "
  [1]=>
  string(21) "gb_range_start <= '0'"
  [2]=>
  string(19) "gb_range_end >= '0'"
  [3]=>
  string(28) "gb_expiry > '20190726164630'"
}
This comment was removed by Reedy.

I can reproduce this by creating a GlobalBlock of an IP range via the API.

I initially read this as making a query for global blocks, but it's actually the action of making a block

For the 'target' parameter...

	"apihelp-globalblock-param-target": "The target IP address.",

This suggests it should only accept (single) IPs, not ranges, CIDR or anything else.

I guess, at a minimum, it should error out gracefully if a range is passed... And then potentially a future feature request make it accept ranges and CIDR?

IP::isValid() looks to do the job to make sure it's not a range at least

Niharika changed the subtype of this task from "Task" to "Bug Report".Jul 31 2019, 3:19 PM
Niharika subscribed.

This doesn't seem to have come about from AHT's work on blocks. We're prioritizing more important tasks over this at the moment.

mmodell changed the subtype of this task from "Bug Report" to "Production Error".Aug 28 2019, 11:09 PM

Change 546506 had a related patch set uploaded (by Tchanders; owner: Tchanders):
[mediawiki/extensions/GlobalBlocking@master] Update GlobalBlocking::getRangeCondition to work with IP ranges

https://gerrit.wikimedia.org/r/546506

Change 546510 had a related patch set uploaded (by Tchanders; owner: Tchanders):
[mediawiki/extensions/GlobalBlocking@master] Check for block before accessing block's properties in ApiGlobalBlock

https://gerrit.wikimedia.org/r/546510

GlobalBlocking is documented to be able to block IP ranges: https://www.mediawiki.org/wiki/Extension:GlobalBlocking and is certainly used that way, e.g.: https://en.wikipedia.org/wiki/Special:GlobalBlockList

On local testing with the examples given by @Reedy above, the blocks are made successfully, before ApiGlobalBlock::execute errors out. (These examples fail because for IPv4, IP::toHex calls ip2long(), which fails on a range. Confusingly IP::toHex happens to work on an IPv6 range, so perhaps it should be documented that it is not meant for IP ranges.)

GlobalBlocking::getRangeCondition is called on retrieving a block, not inserting one. It may be passed an IP address or an IP range, but is not currently set up to deal with a range.

What should be done:

  • GlobalBlocking::getRangeCondition should be updated to use IP::parseRange
  • ApiGlobalBlock::execute could be updated to check for a block before accessing properties

Change 546506 merged by jenkins-bot:
[mediawiki/extensions/GlobalBlocking@master] Update GlobalBlocking::getRangeCondition to work with IP ranges

https://gerrit.wikimedia.org/r/546506

Change 546510 merged by jenkins-bot:
[mediawiki/extensions/GlobalBlocking@master] Check for block before accessing block's properties in ApiGlobalBlock

https://gerrit.wikimedia.org/r/546510

Krinkle assigned this task to Tchanders.