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 @@ -157,6 +157,8 @@ PHP NEWS
rmdir(), touch(), chmod() or stat(). (Ilia Alshanetsky)

- Standard:
. Fixed pack() accepting values that cannot be converted to int or float for
integer and floating-point format codes. (Weilin Du)
. Fixed quoted_printable_decode() truncating input at NUL bytes. (Weilin Du)
. Fixed version_compare() silently truncating version strings at NUL bytes by
rejecting such input. (Weilin Du)
Expand Down
9 changes: 9 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ PHP 8.7 UPGRADE NOTES
application terminate one attribute and append further ones.

- 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.
. The number of filters that can be chained in a php://filter URL is limited
to 16 by default. Set the stream context option max_filter_count to change
this.
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 @@ -617,7 +649,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 @@ -639,7 +678,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 @@ -648,7 +694,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 @@ -670,7 +724,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 @@ -694,7 +755,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 @@ -705,7 +773,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 @@ -722,7 +798,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 @@ -757,11 +839,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