Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ PHP NEWS
- Standard:
. Fixed a segfault when a stream filter callback unsets StreamBucket::$data
before re-attaching the bucket. (iliaal)
. Fixed pack() accepting values that cannot be converted to int or float for
integer and floating-point format codes. (Weilin Du)
. Fixed an out-of-bounds read when following a redirect response with an
empty Location header. (iliaal)
. Fixed read buffer compaction in php_stream_filter_flush(). (crystarm)
Expand Down
9 changes: 9 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,15 @@ PHP 8.6 UPGRADE NOTES
and DirectoryIterator::current() returns string|SplFileInfo|static.

- Standard:
. pack() now throws a TypeError when a value for an integer or floating-point
format code cannot be converted to int or float, instead of silently
coercing it. Floating-point format codes use PHP numeric-string syntax, so
"INF" and "NAN" strings are no longer accepted; the corresponding float
values remain accepted. Integer format codes now emit E_DEPRECATED when
converting a float or float-string to int loses precision. Integer and
floating-point format codes emit E_WARNING for leading-numeric strings.
These diagnostics are propagated if an error handler converts them to
exceptions.
. array_intersect() with at least two arrays now converts values to strings
while scanning its inputs instead of during sort comparisons. This can
change the number and order of conversion warnings and __toString() calls,
Expand Down
42 changes: 42 additions & 0 deletions ext/gmp/tests/pack_float_conversion.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
pack() accepts GMP values for numeric format codes
--EXTENSIONS--
gmp
--FILE--
<?php

$integerFormats = ['c', 'C', 's', 'S', 'n', 'v', 'i', 'I', 'l', 'L', 'N', 'V'];
if (PHP_INT_SIZE >= 8) {
array_push($integerFormats, 'q', 'Q', 'J', 'P');
}
$floatFormats = ['f', 'g', 'G', 'd', 'e', 'E'];
$value = gmp_init(42);

$passed = true;
foreach ($integerFormats as $format) {
$actual = unpack($format, pack($format, $value))[1];
if ($actual !== 42) {
echo "Unexpected result for $format: ";
var_dump($actual);
$passed = false;
}
}
echo "integer formats: ";
var_dump($passed);

$passed = true;
foreach ($floatFormats as $format) {
$actual = unpack($format, pack($format, $value))[1];
if ($actual !== 42.0) {
echo "Unexpected result for $format: ";
var_dump($actual);
$passed = false;
}
}
echo "floating-point formats: ";
var_dump($passed);

?>
--EXPECT--
integer formats: bool(true)
floating-point formats: bool(true)
110 changes: 99 additions & 11 deletions ext/standard/pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,21 @@ typedef ZEND_SET_ALIGNED(1, unsigned int unaligned_uint);
typedef ZEND_SET_ALIGNED(1, int unaligned_int);

/* {{{ php_pack */
static void php_pack(const zval *val, size_t size, php_pack_endianness endianness, char *output)
static bool php_pack(
const zval *val, uint32_t arg_num, char format_code, size_t size,
php_pack_endianness endianness, char *output
)
{
zend_ulong zl = zval_get_long(val);
bool failed;
zend_ulong zl = zval_try_get_long(val, &failed);

if (UNEXPECTED(failed)) {
zend_argument_type_error(
arg_num, "must be of type int for format code '%c', %s given",
format_code, zend_zval_value_name(val)
);
return false;
}

if ((endianness == PHP_LITTLE_ENDIAN) != MACHINE_LITTLE_ENDIAN) {
zl = PHP_LONG_BSWAP(zl);
Expand All @@ -71,9 +83,28 @@ static void php_pack(const zval *val, size_t size, php_pack_endianness endiannes
}

memcpy(output, (const char *) &zl, size);
return true;
}
/* }}} */

static bool php_pack_try_get_double(
const zval *value, uint32_t arg_num, char format_code, double *result
)
{
bool failed;

*result = zval_try_get_double(value, &failed);
if (UNEXPECTED(failed)) {
zend_argument_type_error(
arg_num, "must be of type float for format code '%c', %s given",
format_code, zend_zval_value_name(value)
);
return false;
}

return true;
}

ZEND_ATTRIBUTE_CONST static inline uint16_t php_pack_reverse_int16(uint16_t arg)
{
return ((arg & 0xFF) << 8) | ((arg >> 8) & 0xFF);
Expand Down Expand Up @@ -211,6 +242,7 @@ PHP_FUNCTION(pack)
size_t formatcount = 0;
int outputpos = 0, outputsize = 0;
zend_string *output;
bool conversion_failed = false;

ZEND_PARSE_PARAMETERS_START(1, -1)
Z_PARAM_STRING(format, formatlen)
Expand Down Expand Up @@ -614,7 +646,14 @@ PHP_FUNCTION(pack)
case 'c':
case 'C':
while (arg-- > 0) {
php_pack(&argv[currentarg++], 1, PHP_MACHINE_ENDIAN, &ZSTR_VAL(output)[outputpos]);
uint32_t arg_num = currentarg + 2;
if (!php_pack(
&argv[currentarg], arg_num, code, 1, PHP_MACHINE_ENDIAN, &ZSTR_VAL(output)[outputpos]
)) {
conversion_failed = true;
goto cleanup;
}
currentarg++;
outputpos++;
}
break;
Expand All @@ -636,7 +675,14 @@ PHP_FUNCTION(pack)
}

while (arg-- > 0) {
php_pack(&argv[currentarg++], 2, endianness, &ZSTR_VAL(output)[outputpos]);
uint32_t arg_num = currentarg + 2;
if (!php_pack(
&argv[currentarg], arg_num, code, 2, endianness, &ZSTR_VAL(output)[outputpos]
)) {
conversion_failed = true;
goto cleanup;
}
currentarg++;
outputpos += 2;
}
break;
Expand All @@ -645,7 +691,15 @@ PHP_FUNCTION(pack)
case 'i':
case 'I':
while (arg-- > 0) {
php_pack(&argv[currentarg++], sizeof(int), PHP_MACHINE_ENDIAN, &ZSTR_VAL(output)[outputpos]);
uint32_t arg_num = currentarg + 2;
if (!php_pack(
&argv[currentarg], arg_num, code, sizeof(int), PHP_MACHINE_ENDIAN,
&ZSTR_VAL(output)[outputpos]
)) {
conversion_failed = true;
goto cleanup;
}
currentarg++;
outputpos += sizeof(int);
}
break;
Expand All @@ -667,7 +721,14 @@ PHP_FUNCTION(pack)
}

while (arg-- > 0) {
php_pack(&argv[currentarg++], 4, endianness, &ZSTR_VAL(output)[outputpos]);
uint32_t arg_num = currentarg + 2;
if (!php_pack(
&argv[currentarg], arg_num, code, 4, endianness, &ZSTR_VAL(output)[outputpos]
)) {
conversion_failed = true;
goto cleanup;
}
currentarg++;
outputpos += 4;
}
break;
Expand All @@ -691,7 +752,14 @@ PHP_FUNCTION(pack)
}

while (arg-- > 0) {
php_pack(&argv[currentarg++], 8, endianness, &ZSTR_VAL(output)[outputpos]);
uint32_t arg_num = currentarg + 2;
if (!php_pack(
&argv[currentarg], arg_num, code, 8, endianness, &ZSTR_VAL(output)[outputpos]
)) {
conversion_failed = true;
goto cleanup;
}
currentarg++;
outputpos += 8;
}
break;
Expand All @@ -702,7 +770,15 @@ PHP_FUNCTION(pack)
case 'g':
case 'G': {
while (arg-- > 0) {
float v = (float) zval_get_double(&argv[currentarg++]);
double d;
float v;
uint32_t arg_num = currentarg + 2;
if (!php_pack_try_get_double(&argv[currentarg], arg_num, code, &d)) {
conversion_failed = true;
goto cleanup;
}
currentarg++;
v = (float) d;
if (code == 'g' || formatendian[i] == PHP_LITTLE_ENDIAN) {
php_pack_copy_float(1, &ZSTR_VAL(output)[outputpos], v);
} else if (code == 'G' || formatendian[i] == PHP_BIG_ENDIAN) {
Expand All @@ -719,7 +795,13 @@ PHP_FUNCTION(pack)
case 'e':
case 'E': {
while (arg-- > 0) {
double v = zval_get_double(&argv[currentarg++]);
double v;
uint32_t arg_num = currentarg + 2;
if (!php_pack_try_get_double(&argv[currentarg], arg_num, code, &v)) {
conversion_failed = true;
goto cleanup;
}
currentarg++;
if (code == 'e' || formatendian[i] == PHP_LITTLE_ENDIAN) {
php_pack_copy_double(1, &ZSTR_VAL(output)[outputpos], v);
} else if (code == 'E' || formatendian[i] == PHP_BIG_ENDIAN) {
Expand Down Expand Up @@ -754,11 +836,17 @@ PHP_FUNCTION(pack)
}
}

ZSTR_VAL(output)[outputpos] = '\0';
ZSTR_LEN(output) = outputpos;

cleanup:
efree(formatcodes);
efree(formatargs);
efree(formatendian);
ZSTR_VAL(output)[outputpos] = '\0';
ZSTR_LEN(output) = outputpos;
if (UNEXPECTED(conversion_failed)) {
zend_string_release(output);
RETURN_THROWS();
}
RETURN_NEW_STR(output);
}
/* }}} */
Expand Down
24 changes: 0 additions & 24 deletions ext/standard/tests/strings/pack_float.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ pack()/unpack(): float/double tests
<?php
var_dump(
'pack e',
bin2hex(pack("e", "")),
bin2hex(pack("e", "a")),
bin2hex(pack("e", " ")),
bin2hex(pack("e", NULL)),
bin2hex(pack("e", 0)),
bin2hex(pack("e", 1)),
Expand All @@ -21,9 +18,6 @@ var_dump(
bin2hex(pack("e", -12345678901234567890.1234567898765432123456789)),

'pack E',
bin2hex(pack("E", "")),
bin2hex(pack("E", "a")),
bin2hex(pack("E", " ")),
bin2hex(pack("E", NULL)),
bin2hex(pack("E", 0)),
bin2hex(pack("E", 1)),
Expand All @@ -38,9 +32,6 @@ var_dump(
bin2hex(pack("E", -12345678901234567890.1234567898765432123456789)),

'pack g',
bin2hex(pack("g", "")),
bin2hex(pack("g", "a")),
bin2hex(pack("g", " ")),
bin2hex(pack("g", NULL)),
bin2hex(pack("g", 0)),
bin2hex(pack("g", 1)),
Expand All @@ -55,9 +46,6 @@ var_dump(
bin2hex(pack("g", -12345678901234567890.1234567898765432123456789)),

'pack G',
bin2hex(pack("G", "")),
bin2hex(pack("G", "a")),
bin2hex(pack("G", " ")),
bin2hex(pack("G", NULL)),
bin2hex(pack("G", 0)),
bin2hex(pack("G", 1)),
Expand Down Expand Up @@ -117,9 +105,6 @@ var_dump(
string(6) "pack e"
string(16) "0000000000000000"
string(16) "0000000000000000"
string(16) "0000000000000000"
string(16) "0000000000000000"
string(16) "0000000000000000"
string(16) "000000000000f03f"
string(16) "000000000000f03f"
string(16) "0080e03779c34143"
Expand All @@ -133,9 +118,6 @@ string(16) "e1639d31956ae5c3"
string(6) "pack E"
string(16) "0000000000000000"
string(16) "0000000000000000"
string(16) "0000000000000000"
string(16) "0000000000000000"
string(16) "0000000000000000"
string(16) "3ff0000000000000"
string(16) "3ff0000000000000"
string(16) "4341c37937e08000"
Expand All @@ -149,9 +131,6 @@ string(16) "c3e56a95319d63e1"
string(6) "pack g"
string(8) "00000000"
string(8) "00000000"
string(8) "00000000"
string(8) "00000000"
string(8) "00000000"
string(8) "0000803f"
string(8) "0000803f"
string(8) "ca1b0e5a"
Expand All @@ -165,9 +144,6 @@ string(8) "aa542bdf"
string(6) "pack G"
string(8) "00000000"
string(8) "00000000"
string(8) "00000000"
string(8) "00000000"
string(8) "00000000"
string(8) "3f800000"
string(8) "3f800000"
string(8) "5a0e1bca"
Expand Down
Loading
Loading