php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #47854 explode() misbehaves with very large negative limit
Submitted: 2009-03-31 19:15 UTC Modified: 2010-05-19 11:03 UTC
From: disas at mail dot ru Assigned: mike (profile)
Status: Closed Package: Strings related
PHP Version: 5.*, 6CVS (2009-04-01) OS: *
Private report: No CVE-ID: None
 [2009-03-31 19:15 UTC] disas at mail dot ru
Description:
------------
Bug on result where negative or positive number of limit is very high.

Reproduce code:
---------------
#Code:

var_export(
	explode(" "," 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ", -1e99)
);

#Result:
array (
  0 => ' 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ',
);

#Code:

var_export(
	explode(" "," 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ", -1e9)
);

#Result:
array (
)

Expected result:
----------------
array (
  0 => ' 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ',
);

Actual result:
--------------
I think that in both cases the result should be:
array(
)

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-04-01 11:00 UTC] jani@php.net
Very strange results with HEAD/PHP_5_3 too:

# build/php_6/sapi/cli/php -n -dmemory_limit=2G -r 
'var_dump(explode(" "," 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ", 
-1e99));'

Fatal error: Allowed memory size of 2147483648 bytes exhausted (tried 
to allocate 18446744073383068122 bytes) in Command line code on line 
1

# build/php_5_3/sapi/cli/php -n -dmemory_limit=2G -r 
'var_dump(explode(" "," 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ", 
-1e99));'

Fatal error: Allowed memory size of 2147483648 bytes exhausted (tried 
to allocate 3792765909 bytes) in Command line code on line 1

# build/php_5_2/sapi/cli/php -n -dmemory_limit=2G -r 
'var_dump(explode(" "," 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ", 
-1e99));'
array(1) {
  [0]=>
  string(43) " 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 "
}


 [2009-04-15 15:02 UTC] mattwil@php.net
What's happening with -1e9 is that it's small enough to fit in the range of an integer (which explode()'s limit parameter expects). (Other larger numbers that aren't in the integer range may or may not behave as expected -- if there's integer overflow/"wraparound", with -3e9 for example, negative may become positive, or vice versa, giving different behavior than the sign of limit would suggest.)

However, when you give a much larger number like -1e99 and it gets cast to an integer, it actually just becomes 0 which explains the behavior you're seeing.

Additional info that the bug reporter can ignore if they want:

That explanation applies to PHP 5.2 and prior. But in 5.3 as of right now, on *most* platforms (except Win64), *most* numbers out of integer range (except between 2^31 and 2^32-1 on 32-bit platforms) have a "hard limit" applied that keeps them at the negative/positive integer limit, so no matter how large of a number you pass to explode(), even infinity, it ends up being the integer limit (with same sign). Like I said, this varies by platform, but that's the general behavior in 5.3 right now.

Overall, 5.3's conversion of large floats is currently inconsistent, flawed, and changes prior behavior that's been around for years, which is why I'm trying to get it sorted out. Now in cases like this, with function parameters like a "limit," the new behavior makes sense (which only happens in *most* cases, thanks to platform inconsistencies).

But wait, it gets better: Don't pass the parameter as a number, but as a string, e.g. '-1e99', and you'll probably get a different result still! Again, depending on platform -- absolute difference between platforms and/or relative difference compared to a non-string version.

What a mess... And this applies to basically every function in 5.3 that takes an integer parameter. My proposed changes take care of these things and allow functions, like explode(), to use a new integer conversion specifier, where desired, to limit a parameter to the range of an integer *consistently*. Functions that aren't updated would simply behave like 5.2 and prior.
 [2009-04-15 15:05 UTC] mattwil@php.net
Jani, as you can tell from my explanation, -1e99 becomes 0 with 5.2, but LONG_MIN with 5.3/6. In the explode() function, there is/was a int/long mismatch, so overflow issues on 64-bit, causing an unexpected value of "limit" in php_explode_negative_limit() and, I'm guessing, an invalid read of "positions" there.

I fixed the long/int mismatch in 5.3/6 a couple weeks ago, so I assume the strange results are gone? BTW, since 5.2 isn't using long where needed, you should be able to break it (on 64-bit) by passing -PHP_INT_MAX - 1 instead of -1e99.
 [2010-05-19 11:03 UTC] mike@php.net
-Status: Verified +Status: Closed -Assigned To: +Assigned To: mike
 [2010-05-19 11:03 UTC] mike@php.net
Closed then, eh?
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Mon Dec 30 16:01:29 2024 UTC