mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Merge branch 'PHP-8.1' into PHP-8.2
* PHP-8.1: Update NEWS with info about security issues Fix GHSA-www2-q4fc-65wf Fix GHSA-h96m-rvf9-jgm2 Fix GHSA-8xr5-qppj-gvwj: PDO quoting result null deref Fix GH-20584: Information Leak of Memory
This commit is contained in:
12
NEWS
12
NEWS
@@ -10,6 +10,18 @@ PHP NEWS
|
||||
. Reset global pointers to prevent use-after-free in zend_jit_status().
|
||||
(Florian Engelhardt)
|
||||
|
||||
- PDO:
|
||||
. Fixed GHSA-8xr5-qppj-gvwj (PDO quoting result null deref). (CVE-2025-14180)
|
||||
(Jakub Zelenka)
|
||||
|
||||
- Standard:
|
||||
. Fixed GHSA-www2-q4fc-65wf (Null byte termination in dns_get_record()).
|
||||
(ndossche)
|
||||
. Fixed GHSA-h96m-rvf9-jgm2 (Heap buffer overflow in array_merge()).
|
||||
(CVE-2025-14178) (ndossche)
|
||||
. Fixed GHSA-3237-qqm7-mfv7 (Information Leak of Memory in getimagesize).
|
||||
(CVE-2025-14177) (ndossche)
|
||||
|
||||
03 Jul 2025, PHP 8.2.29
|
||||
|
||||
- PGSQL:
|
||||
|
||||
@@ -287,6 +287,12 @@ safe:
|
||||
}
|
||||
|
||||
plc->quoted = stmt->dbh->methods->quoter(stmt->dbh, buf, param_type);
|
||||
if (plc->quoted == NULL) {
|
||||
/* bork */
|
||||
ret = -1;
|
||||
strncpy(stmt->error_code, stmt->dbh->error_code, 6);
|
||||
goto clean_up;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
28
ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt
Normal file
28
ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt
Normal file
@@ -0,0 +1,28 @@
|
||||
--TEST--
|
||||
#GHSA-8xr5-qppj-gvwj: NULL Pointer Derefernce for failed user input quoting
|
||||
--EXTENSIONS--
|
||||
pdo
|
||||
pdo_pgsql
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
|
||||
require_once dirname(__FILE__) . '/config.inc';
|
||||
PDOTest::skip();
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
|
||||
require_once dirname(__FILE__) . '/config.inc';
|
||||
$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
|
||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
|
||||
|
||||
$sql = "SELECT * FROM users where username = :username";
|
||||
$stmt = $db->prepare($sql);
|
||||
|
||||
$p1 = "alice\x99";
|
||||
var_dump($stmt->execute(['username' => $p1]));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(false)
|
||||
@@ -3903,7 +3903,7 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET
|
||||
int argc, i;
|
||||
zval *src_entry;
|
||||
HashTable *src, *dest;
|
||||
uint32_t count = 0;
|
||||
uint64_t count = 0;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(0, -1)
|
||||
Z_PARAM_VARIADIC('+', args, argc)
|
||||
@@ -3923,6 +3923,11 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET
|
||||
count += zend_hash_num_elements(Z_ARRVAL_P(arg));
|
||||
}
|
||||
|
||||
if (UNEXPECTED(count >= HT_MAX_SIZE)) {
|
||||
zend_throw_error(NULL, "The total number of elements must be lower than %u", HT_MAX_SIZE);
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
if (argc == 2) {
|
||||
zval *ret = NULL;
|
||||
|
||||
|
||||
@@ -578,7 +578,7 @@ PHP_FUNCTION(inet_pton)
|
||||
char buffer[17];
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(1, 1)
|
||||
Z_PARAM_STRING(address, address_len)
|
||||
Z_PARAM_PATH(address, address_len)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
@@ -615,7 +615,7 @@ PHP_FUNCTION(ip2long)
|
||||
#endif
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(1, 1)
|
||||
Z_PARAM_STRING(addr, addr_len)
|
||||
Z_PARAM_PATH(addr, addr_len)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
#ifdef HAVE_INET_PTON
|
||||
@@ -2214,8 +2214,8 @@ PHP_FUNCTION(getservbyname)
|
||||
struct servent *serv;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(2, 2)
|
||||
Z_PARAM_STR(name)
|
||||
Z_PARAM_STRING(proto, proto_len)
|
||||
Z_PARAM_PATH_STR(name)
|
||||
Z_PARAM_PATH(proto, proto_len)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
|
||||
@@ -2258,7 +2258,7 @@ PHP_FUNCTION(getservbyport)
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(2, 2)
|
||||
Z_PARAM_LONG(port)
|
||||
Z_PARAM_STRING(proto, proto_len)
|
||||
Z_PARAM_PATH(proto, proto_len)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
serv = getservbyport(htons((unsigned short) port), proto);
|
||||
@@ -2285,7 +2285,7 @@ PHP_FUNCTION(getprotobyname)
|
||||
struct protoent *ent;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(1, 1)
|
||||
Z_PARAM_STRING(name, name_len)
|
||||
Z_PARAM_PATH(name, name_len)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
ent = getprotobyname(name);
|
||||
|
||||
@@ -379,7 +379,7 @@ PHP_FUNCTION(dns_check_record)
|
||||
#endif
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(1, 2)
|
||||
Z_PARAM_STRING(hostname, hostname_len)
|
||||
Z_PARAM_PATH(hostname, hostname_len)
|
||||
Z_PARAM_OPTIONAL
|
||||
Z_PARAM_STR(rectype)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
@@ -826,7 +826,7 @@ PHP_FUNCTION(dns_get_record)
|
||||
bool raw = 0;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(1, 5)
|
||||
Z_PARAM_STRING(hostname, hostname_len)
|
||||
Z_PARAM_PATH(hostname, hostname_len)
|
||||
Z_PARAM_OPTIONAL
|
||||
Z_PARAM_LONG(type_param)
|
||||
Z_PARAM_ZVAL(authns)
|
||||
@@ -1064,7 +1064,7 @@ PHP_FUNCTION(dns_get_mx)
|
||||
#endif
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(2, 3)
|
||||
Z_PARAM_STRING(hostname, hostname_len)
|
||||
Z_PARAM_PATH(hostname, hostname_len)
|
||||
Z_PARAM_ZVAL(mx_list)
|
||||
Z_PARAM_OPTIONAL
|
||||
Z_PARAM_ZVAL(weight_list)
|
||||
|
||||
@@ -48,7 +48,7 @@ PHP_FUNCTION(dns_get_mx) /* {{{ */
|
||||
DNS_STATUS status; /* Return value of DnsQuery_A() function */
|
||||
PDNS_RECORD pResult, pRec; /* Pointer to DNS_RECORD structure */
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|z", &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) {
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "pz|z", &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ PHP_FUNCTION(dns_check_record)
|
||||
DNS_STATUS status; /* Return value of DnsQuery_A() function */
|
||||
PDNS_RECORD pResult; /* Pointer to DNS_RECORD structure */
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|S", &hostname, &hostname_len, &rectype) == FAILURE) {
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|S", &hostname, &hostname_len, &rectype) == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ PHP_FUNCTION(dns_get_record)
|
||||
int type, type_to_fetch, first_query = 1, store_results = 1;
|
||||
bool raw = 0;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lz!z!b",
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|lz!z!b",
|
||||
&hostname, &hostname_len, &type_param, &authns, &addtl, &raw) == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
@@ -403,6 +403,7 @@ static size_t php_read_stream_all_chunks(php_stream *stream, char *buffer, size_
|
||||
if (read_now < stream->chunk_size && read_total != length) {
|
||||
return 0;
|
||||
}
|
||||
buffer += read_now;
|
||||
} while (read_total < length);
|
||||
|
||||
return read_total;
|
||||
|
||||
16
ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt
Normal file
16
ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt
Normal file
@@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
GHSA-h96m-rvf9-jgm2
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$power = 20; // Chosen to be well within a memory_limit
|
||||
$arr = range(0, 2**$power);
|
||||
try {
|
||||
array_merge(...array_fill(0, 2**(32-$power), $arr));
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
The total number of elements must be lower than %d
|
||||
39
ext/standard/tests/image/gh20584.phpt
Normal file
39
ext/standard/tests/image/gh20584.phpt
Normal file
@@ -0,0 +1,39 @@
|
||||
--TEST--
|
||||
GH-20584 (Information Leak of Memory)
|
||||
--CREDITS--
|
||||
Nikita Sveshnikov (Positive Technologies)
|
||||
--FILE--
|
||||
<?php
|
||||
// Minimal PoC: corruption/uninitialized memory leak when reading APP1 via php://filter
|
||||
$file = __DIR__ . '/gh20584.jpg';
|
||||
|
||||
// Make APP1 large enough so it is read in multiple chunks
|
||||
$chunk = 8192;
|
||||
$tail = 123;
|
||||
$payload = str_repeat('A', $chunk) . str_repeat('B', $chunk) . str_repeat('Z',
|
||||
$tail);
|
||||
$app1Len = 2 + strlen($payload);
|
||||
|
||||
// Minimal JPEG: SOI + APP1 + SOF0(1x1) + EOI
|
||||
$sof = "\xFF\xC0" . pack('n', 11) . "\x08" . pack('n',1) . pack('n',1) .
|
||||
"\x01\x11\x00";
|
||||
$jpeg = "\xFF\xD8" . "\xFF\xE1" . pack('n', $app1Len) . $payload . $sof .
|
||||
"\xFF\xD9";
|
||||
file_put_contents($file, $jpeg);
|
||||
|
||||
// Read through a filter to enforce multiple reads
|
||||
$src = 'php://filter/read=string.rot13|string.rot13/resource=' . $file;
|
||||
$info = null;
|
||||
@getimagesize($src, $info);
|
||||
$exp = $payload;
|
||||
$ret = $info['APP1'];
|
||||
|
||||
var_dump($ret === $exp);
|
||||
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
@unlink(__DIR__ . '/gh20584.jpg');
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
62
ext/standard/tests/network/ghsa-www2-q4fc-65wf.phpt
Normal file
62
ext/standard/tests/network/ghsa-www2-q4fc-65wf.phpt
Normal file
@@ -0,0 +1,62 @@
|
||||
--TEST--
|
||||
GHSA-www2-q4fc-65wf
|
||||
--DESCRIPTION--
|
||||
This is a ZPP test but *keep* this as it is security-sensitive!
|
||||
--FILE--
|
||||
<?php
|
||||
try {
|
||||
dns_check_record("\0");
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
dns_get_mx("\0", $out);
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
dns_get_record("\0");
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
getprotobyname("\0");
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
getservbyname("\0", "tcp");
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
getservbyname("x", "tcp\0");
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
getservbyport(0, "tcp\0");
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
inet_pton("\0");
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
ip2long("\0");
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
dns_check_record(): Argument #1 ($hostname) must not contain any null bytes
|
||||
dns_get_mx(): Argument #1 ($hostname) must not contain any null bytes
|
||||
dns_get_record(): Argument #1 ($hostname) must not contain any null bytes
|
||||
getprotobyname(): Argument #1 ($protocol) must not contain any null bytes
|
||||
getservbyname(): Argument #1 ($service) must not contain any null bytes
|
||||
getservbyname(): Argument #2 ($protocol) must not contain any null bytes
|
||||
getservbyport(): Argument #2 ($protocol) must not contain any null bytes
|
||||
inet_pton(): Argument #1 ($ip) must not contain any null bytes
|
||||
ip2long(): Argument #1 ($ip) must not contain any null bytes
|
||||
Reference in New Issue
Block a user