Skip to content

Commit 6e124d0

Browse files
committed
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
2 parents 06b8b75 + c48a9f4 commit 6e124d0

File tree

11 files changed

+182
-13
lines changed

11 files changed

+182
-13
lines changed

NEWS

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ PHP NEWS
1010
. Reset global pointers to prevent use-after-free in zend_jit_status().
1111
(Florian Engelhardt)
1212

13+
- PDO:
14+
. Fixed GHSA-8xr5-qppj-gvwj (PDO quoting result null deref). (CVE-2025-14180)
15+
(Jakub Zelenka)
16+
17+
- Standard:
18+
. Fixed GHSA-www2-q4fc-65wf (Null byte termination in dns_get_record()).
19+
(ndossche)
20+
. Fixed GHSA-h96m-rvf9-jgm2 (Heap buffer overflow in array_merge()).
21+
(CVE-2025-14178) (ndossche)
22+
. Fixed GHSA-3237-qqm7-mfv7 (Information Leak of Memory in getimagesize).
23+
(CVE-2025-14177) (ndossche)
24+
1325
03 Jul 2025, PHP 8.2.29
1426

1527
- PGSQL:

ext/pdo/pdo_sql_parser.re

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ safe:
287287
}
288288

289289
plc->quoted = stmt->dbh->methods->quoter(stmt->dbh, buf, param_type);
290+
if (plc->quoted == NULL) {
291+
/* bork */
292+
ret = -1;
293+
strncpy(stmt->error_code, stmt->dbh->error_code, 6);
294+
goto clean_up;
295+
}
290296
}
291297
}
292298

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
#GHSA-8xr5-qppj-gvwj: NULL Pointer Derefernce for failed user input quoting
3+
--EXTENSIONS--
4+
pdo
5+
pdo_pgsql
6+
--SKIPIF--
7+
<?php
8+
require_once dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
9+
require_once dirname(__FILE__) . '/config.inc';
10+
PDOTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
require_once dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
15+
require_once dirname(__FILE__) . '/config.inc';
16+
$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
17+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
18+
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
19+
20+
$sql = "SELECT * FROM users where username = :username";
21+
$stmt = $db->prepare($sql);
22+
23+
$p1 = "alice\x99";
24+
var_dump($stmt->execute(['username' => $p1]));
25+
26+
?>
27+
--EXPECT--
28+
bool(false)

ext/standard/array.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3903,7 +3903,7 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET
39033903
int argc, i;
39043904
zval *src_entry;
39053905
HashTable *src, *dest;
3906-
uint32_t count = 0;
3906+
uint64_t count = 0;
39073907

39083908
ZEND_PARSE_PARAMETERS_START(0, -1)
39093909
Z_PARAM_VARIADIC('+', args, argc)
@@ -3923,6 +3923,11 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET
39233923
count += zend_hash_num_elements(Z_ARRVAL_P(arg));
39243924
}
39253925

3926+
if (UNEXPECTED(count >= HT_MAX_SIZE)) {
3927+
zend_throw_error(NULL, "The total number of elements must be lower than %u", HT_MAX_SIZE);
3928+
RETURN_THROWS();
3929+
}
3930+
39263931
if (argc == 2) {
39273932
zval *ret = NULL;
39283933

ext/standard/basic_functions.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ PHP_FUNCTION(inet_pton)
578578
char buffer[17];
579579

580580
ZEND_PARSE_PARAMETERS_START(1, 1)
581-
Z_PARAM_STRING(address, address_len)
581+
Z_PARAM_PATH(address, address_len)
582582
ZEND_PARSE_PARAMETERS_END();
583583

584584
memset(buffer, 0, sizeof(buffer));
@@ -615,7 +615,7 @@ PHP_FUNCTION(ip2long)
615615
#endif
616616

617617
ZEND_PARSE_PARAMETERS_START(1, 1)
618-
Z_PARAM_STRING(addr, addr_len)
618+
Z_PARAM_PATH(addr, addr_len)
619619
ZEND_PARSE_PARAMETERS_END();
620620

621621
#ifdef HAVE_INET_PTON
@@ -2214,8 +2214,8 @@ PHP_FUNCTION(getservbyname)
22142214
struct servent *serv;
22152215

22162216
ZEND_PARSE_PARAMETERS_START(2, 2)
2217-
Z_PARAM_STR(name)
2218-
Z_PARAM_STRING(proto, proto_len)
2217+
Z_PARAM_PATH_STR(name)
2218+
Z_PARAM_PATH(proto, proto_len)
22192219
ZEND_PARSE_PARAMETERS_END();
22202220

22212221

@@ -2258,7 +2258,7 @@ PHP_FUNCTION(getservbyport)
22582258

22592259
ZEND_PARSE_PARAMETERS_START(2, 2)
22602260
Z_PARAM_LONG(port)
2261-
Z_PARAM_STRING(proto, proto_len)
2261+
Z_PARAM_PATH(proto, proto_len)
22622262
ZEND_PARSE_PARAMETERS_END();
22632263

22642264
serv = getservbyport(htons((unsigned short) port), proto);
@@ -2285,7 +2285,7 @@ PHP_FUNCTION(getprotobyname)
22852285
struct protoent *ent;
22862286

22872287
ZEND_PARSE_PARAMETERS_START(1, 1)
2288-
Z_PARAM_STRING(name, name_len)
2288+
Z_PARAM_PATH(name, name_len)
22892289
ZEND_PARSE_PARAMETERS_END();
22902290

22912291
ent = getprotobyname(name);

ext/standard/dns.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ PHP_FUNCTION(dns_check_record)
379379
#endif
380380

381381
ZEND_PARSE_PARAMETERS_START(1, 2)
382-
Z_PARAM_STRING(hostname, hostname_len)
382+
Z_PARAM_PATH(hostname, hostname_len)
383383
Z_PARAM_OPTIONAL
384384
Z_PARAM_STR(rectype)
385385
ZEND_PARSE_PARAMETERS_END();
@@ -826,7 +826,7 @@ PHP_FUNCTION(dns_get_record)
826826
bool raw = 0;
827827

828828
ZEND_PARSE_PARAMETERS_START(1, 5)
829-
Z_PARAM_STRING(hostname, hostname_len)
829+
Z_PARAM_PATH(hostname, hostname_len)
830830
Z_PARAM_OPTIONAL
831831
Z_PARAM_LONG(type_param)
832832
Z_PARAM_ZVAL(authns)
@@ -1064,7 +1064,7 @@ PHP_FUNCTION(dns_get_mx)
10641064
#endif
10651065

10661066
ZEND_PARSE_PARAMETERS_START(2, 3)
1067-
Z_PARAM_STRING(hostname, hostname_len)
1067+
Z_PARAM_PATH(hostname, hostname_len)
10681068
Z_PARAM_ZVAL(mx_list)
10691069
Z_PARAM_OPTIONAL
10701070
Z_PARAM_ZVAL(weight_list)

ext/standard/dns_win32.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ PHP_FUNCTION(dns_get_mx) /* {{{ */
4848
DNS_STATUS status; /* Return value of DnsQuery_A() function */
4949
PDNS_RECORD pResult, pRec; /* Pointer to DNS_RECORD structure */
5050

51-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|z", &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) {
51+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "pz|z", &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) {
5252
RETURN_THROWS();
5353
}
5454

@@ -102,7 +102,7 @@ PHP_FUNCTION(dns_check_record)
102102
DNS_STATUS status; /* Return value of DnsQuery_A() function */
103103
PDNS_RECORD pResult; /* Pointer to DNS_RECORD structure */
104104

105-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|S", &hostname, &hostname_len, &rectype) == FAILURE) {
105+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|S", &hostname, &hostname_len, &rectype) == FAILURE) {
106106
RETURN_THROWS();
107107
}
108108

@@ -354,7 +354,7 @@ PHP_FUNCTION(dns_get_record)
354354
int type, type_to_fetch, first_query = 1, store_results = 1;
355355
bool raw = 0;
356356

357-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lz!z!b",
357+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|lz!z!b",
358358
&hostname, &hostname_len, &type_param, &authns, &addtl, &raw) == FAILURE) {
359359
RETURN_THROWS();
360360
}

ext/standard/image.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ static size_t php_read_stream_all_chunks(php_stream *stream, char *buffer, size_
403403
if (read_now < stream->chunk_size && read_total != length) {
404404
return 0;
405405
}
406+
buffer += read_now;
406407
} while (read_total < length);
407408

408409
return read_total;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
GHSA-h96m-rvf9-jgm2
3+
--FILE--
4+
<?php
5+
6+
$power = 20; // Chosen to be well within a memory_limit
7+
$arr = range(0, 2**$power);
8+
try {
9+
array_merge(...array_fill(0, 2**(32-$power), $arr));
10+
} catch (Error $e) {
11+
echo $e->getMessage(), "\n";
12+
}
13+
14+
?>
15+
--EXPECTF--
16+
The total number of elements must be lower than %d
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
GH-20584 (Information Leak of Memory)
3+
--CREDITS--
4+
Nikita Sveshnikov (Positive Technologies)
5+
--FILE--
6+
<?php
7+
// Minimal PoC: corruption/uninitialized memory leak when reading APP1 via php://filter
8+
$file = __DIR__ . '/gh20584.jpg';
9+
10+
// Make APP1 large enough so it is read in multiple chunks
11+
$chunk = 8192;
12+
$tail = 123;
13+
$payload = str_repeat('A', $chunk) . str_repeat('B', $chunk) . str_repeat('Z',
14+
$tail);
15+
$app1Len = 2 + strlen($payload);
16+
17+
// Minimal JPEG: SOI + APP1 + SOF0(1x1) + EOI
18+
$sof = "\xFF\xC0" . pack('n', 11) . "\x08" . pack('n',1) . pack('n',1) .
19+
"\x01\x11\x00";
20+
$jpeg = "\xFF\xD8" . "\xFF\xE1" . pack('n', $app1Len) . $payload . $sof .
21+
"\xFF\xD9";
22+
file_put_contents($file, $jpeg);
23+
24+
// Read through a filter to enforce multiple reads
25+
$src = 'php://filter/read=string.rot13|string.rot13/resource=' . $file;
26+
$info = null;
27+
@getimagesize($src, $info);
28+
$exp = $payload;
29+
$ret = $info['APP1'];
30+
31+
var_dump($ret === $exp);
32+
33+
?>
34+
--CLEAN--
35+
<?php
36+
@unlink(__DIR__ . '/gh20584.jpg');
37+
?>
38+
--EXPECT--
39+
bool(true)

0 commit comments

Comments
 (0)