[php-src] master: Merge branch 'PHP-8.5'
Gina Peter Banyard <[email protected]>
| Newsgroups | gmane.comp.php.cvs.general |
|---|---|
| Message-ID | <[email protected]> |
Author: Gina Peter Banyard (Girgias)
Date: 2026-07-02T11:59:10+01:00
Commit: https://github.com/php/php-src/commit/366c38fea0ba029f72c5c8be8baf285c6d2ce0be
Raw diff: https://github.com/php/php-src/commit/366c38fea0ba029f72c5c8be8baf285c6d2ce0be.diff
Merge branch 'PHP-8.5'
* PHP-8.5:
Update NEWS for bugfixes
ext/dba: fix oob read on malformed length field in dba flatfile handler
ext/exif: Fix GH-11020: spurious "Illegal IFD size" warning in exif_read_data()
ext/session: fix GH-21314 (session GC behaviour is different since 8.4)
Changed paths:
A ext/dba/tests/dba_flatfile_oob.phpt
A ext/exif/tests/gh11020.jpg
A ext/exif/tests/gh11020.phpt
M NEWS
M ext/dba/libflatfile/flatfile.c
M ext/exif/exif.c
M ext/exif/tests/bug72094.phpt
M ext/session/php_session.h
M ext/session/session.c
Diff:
diff --git a/NEWS b/NEWS
index ec9efcbe02a3..5f3ea1ff3f61 100644
--- a/NEWS
+++ b/NEWS
@@ -2,9 +2,20 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.6.0alpha2
+- DBA:
+ . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz)
+
+- Exif:
+ . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size"
+ warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman)
+
- OpenSSL:
. Fixed stream_socket_get_crypto_status() after supplemental read. (ilutov)
+- Session:
+ . Fixed bug GH-21314 (Different session garbage collector behavior between
+ PHP 8.3 and PHP 8.5). (jorgsowa)
+
02 Jul 2026, PHP 8.6.0alpha1
- Core:
diff --git a/ext/dba/libflatfile/flatfile.c b/ext/dba/libflatfile/flatfile.c
index 561766777f6f..a5a9bf70c23b 100644
--- a/ext/dba/libflatfile/flatfile.c
+++ b/ext/dba/libflatfile/flatfile.c
@@ -35,6 +35,18 @@
#define FLATFILE_BLOCK_SIZE 1024
+/* Parse the length prefix in `buf` into `num` and grow `buf` to hold it.
+ * atoi() narrows a malformed (e.g. negative) length to a huge size_t whose
+ * `+ FLATFILE_BLOCK_SIZE` would overflow erealloc(); the macro yields true in
+ * that case so the caller stops reading and the read stays within `buf_size`. */
+#define FLATFILE_GROW_BUF(num, buf, buf_size) ( \
+ (num) = atoi(buf), \
+ (num) >= (buf_size) && ( \
+ (num) > SIZE_MAX - FLATFILE_BLOCK_SIZE \
+ || ((buf) = erealloc((buf), (buf_size) = (num) + FLATFILE_BLOCK_SIZE), 0) \
+ ) \
+)
+
/*
* ret = -1 means that database was opened for read-only
* ret = 0 success
@@ -110,10 +122,8 @@ int flatfile_delete(flatfile *dba, datum key_datum) {
if (!php_stream_gets(dba->fp, buf, 15)) {
break;
}
- num = atoi(buf);
- if (num >= buf_size) {
- buf_size = num + FLATFILE_BLOCK_SIZE;
- buf = erealloc(buf, buf_size);
+ if (FLATFILE_GROW_BUF(num, buf, buf_size)) {
+ break;
}
pos = php_stream_tell(dba->fp);
@@ -133,10 +143,8 @@ int flatfile_delete(flatfile *dba, datum key_datum) {
if (!php_stream_gets(dba->fp, buf, 15)) {
break;
}
- num = atoi(buf);
- if (num >= buf_size) {
- buf_size = num + FLATFILE_BLOCK_SIZE;
- buf = erealloc(buf, buf_size);
+ if (FLATFILE_GROW_BUF(num, buf, buf_size)) {
+ break;
}
/* read in the value */
num = php_stream_read(dba->fp, buf, num);
@@ -160,10 +168,8 @@ int flatfile_findkey(flatfile *dba, datum key_datum) {
if (!php_stream_gets(dba->fp, buf, 15)) {
break;
}
- num = atoi(buf);
- if (num >= buf_size) {
- buf_size = num + FLATFILE_BLOCK_SIZE;
- buf = erealloc(buf, buf_size);
+ if (FLATFILE_GROW_BUF(num, buf, buf_size)) {
+ break;
}
num = php_stream_read(dba->fp, buf, num);
@@ -176,10 +182,8 @@ int flatfile_findkey(flatfile *dba, datum key_datum) {
if (!php_stream_gets(dba->fp, buf, 15)) {
break;
}
- num = atoi(buf);
- if (num >= buf_size) {
- buf_size = num + FLATFILE_BLOCK_SIZE;
- buf = erealloc(buf, buf_size);
+ if (FLATFILE_GROW_BUF(num, buf, buf_size)) {
+ break;
}
num = php_stream_read(dba->fp, buf, num);
}
@@ -200,10 +204,8 @@ datum flatfile_firstkey(flatfile *dba) {
if (!php_stream_gets(dba->fp, buf, 15)) {
break;
}
- num = atoi(buf);
- if (num >= buf_size) {
- buf_size = num + FLATFILE_BLOCK_SIZE;
- buf = erealloc(buf, buf_size);
+ if (FLATFILE_GROW_BUF(num, buf, buf_size)) {
+ break;
}
num = php_stream_read(dba->fp, buf, num);
@@ -216,10 +218,8 @@ datum flatfile_firstkey(flatfile *dba) {
if (!php_stream_gets(dba->fp, buf, 15)) {
break;
}
- num = atoi(buf);
- if (num >= buf_size) {
- buf_size = num + FLATFILE_BLOCK_SIZE;
- buf = erealloc(buf, buf_size);
+ if (FLATFILE_GROW_BUF(num, buf, buf_size)) {
+ break;
}
num = php_stream_read(dba->fp, buf, num);
}
@@ -242,20 +242,16 @@ datum flatfile_nextkey(flatfile *dba) {
if (!php_stream_gets(dba->fp, buf, 15)) {
break;
}
- num = atoi(buf);
- if (num >= buf_size) {
- buf_size = num + FLATFILE_BLOCK_SIZE;
- buf = erealloc(buf, buf_size);
+ if (FLATFILE_GROW_BUF(num, buf, buf_size)) {
+ break;
}
num = php_stream_read(dba->fp, buf, num);
if (!php_stream_gets(dba->fp, buf, 15)) {
break;
}
- num = atoi(buf);
- if (num >= buf_size) {
- buf_size = num + FLATFILE_BLOCK_SIZE;
- buf = erealloc(buf, buf_size);
+ if (FLATFILE_GROW_BUF(num, buf, buf_size)) {
+ break;
}
num = php_stream_read(dba->fp, buf, num);
diff --git a/ext/dba/tests/dba_flatfile_oob.phpt b/ext/dba/tests/dba_flatfile_oob.phpt
new file mode 100644
index 000000000000..3328e1dcba90
--- /dev/null
+++ b/ext/dba/tests/dba_flatfile_oob.phpt
@@ -0,0 +1,31 @@
+--TEST--
+DBA FlatFile handler bounds with a malformed (negative) length field
+--EXTENSIONS--
+dba
+--SKIPIF--
+<?php
+require_once __DIR__ . '/setup/setup_dba_tests.inc';
+check_skip('flatfile');
+?>
+--FILE--
+<?php
+$db_file = __DIR__ . '/dba_flatfile_oob.db';
+// A negative length narrows to a huge size_t and previously overran the read buffer.
+file_put_contents($db_file, "-1\n" . str_repeat('A', 200000));
+
+$db = dba_open($db_file, 'r', 'flatfile');
+var_dump(dba_firstkey($db));
+var_dump(dba_exists("AAAA", $db));
+var_dump(dba_fetch("AAAA", $db));
+dba_close($db);
+echo "done\n";
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/dba_flatfile_oob.db');
+?>
+--EXPECT--
+bool(false)
+bool(false)
+bool(false)
+done
diff --git a/ext/exif/exif.c b/ext/exif/exif.c
index a331cbd40cea..31030e12fbf1 100644
--- a/ext/exif/exif.c
+++ b/ext/exif/exif.c
@@ -3650,8 +3650,14 @@ static bool exif_process_IFD_in_JPEG(image_info_type *ImageInfo, char *dir_start
* There are 2 IDFs, the second one holds the keys (0x0201 and 0x0202) to the thumbnail
*/
if (!exif_offset_info_contains(info, dir_start+2+NumDirEntries*12, 4)) {
- exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Illegal IFD size");
- return false;
+ /*
+ * A TIFF/EXIF IFD ends with a 4-byte offset to the next IFD (IFD1 here,
+ * which links the thumbnail), or zero when there is none. Some files end
+ * the EXIF segment right after the entries and omit those 4 bytes. A
+ * missing offset is valid and just means there is no next IFD, so stop
+ * here instead of reporting the size as illegal.
+ */
+ return true;
}
if (tag != TAG_EXIF_IFD_POINTER && tag != TAG_GPS_IFD_POINTER) {
diff --git a/ext/exif/tests/bug72094.phpt b/ext/exif/tests/bug72094.phpt
index c13a85f93f04..8fb3fa97c83d 100644
--- a/ext/exif/tests/bug72094.phpt
+++ b/ext/exif/tests/bug72094.phpt
@@ -47,8 +47,6 @@ Warning: exif_read_data(bug72094_3.jpg): Process tag(x3030=UndefinedTag): Illega
Warning: exif_read_data(bug72094_3.jpg): Process tag(x3030=UndefinedTag): Illegal format code 0x3030, suppose BYTE in %s%ebug72094.php on line %d
-Warning: exif_read_data(bug72094_3.jpg): Illegal IFD size in %s%ebug72094.php on line %d
-
Warning: exif_read_data(bug72094_3.jpg): File structure corrupted in %s%ebug72094.php on line %d
Warning: exif_read_data(bug72094_3.jpg): Invalid JPEG file in %s%ebug72094.php on line %d
diff --git a/ext/exif/tests/gh11020.jpg b/ext/exif/tests/gh11020.jpg
new file mode 100644
index 000000000000..1f978a757613
Binary files /dev/null and b/ext/exif/tests/gh11020.jpg differ
diff --git a/ext/exif/tests/gh11020.phpt b/ext/exif/tests/gh11020.phpt
new file mode 100644
index 000000000000..0c88605749b8
--- /dev/null
+++ b/ext/exif/tests/gh11020.phpt
@@ -0,0 +1,12 @@
+--TEST--
+GH-11020 (exif_read_data() emits a spurious "Illegal IFD size" warning when an IFD is not followed by a next-IFD offset)
+--EXTENSIONS--
+exif
+--FILE--
+<?php
+$data = exif_read_data(__DIR__ . '/gh11020.jpg');
+var_dump(is_array($data), $data['Orientation']);
+?>
+--EXPECT--
+bool(true)
+int(1)
diff --git a/ext/session/php_session.h b/ext/session/php_session.h
index 08c08b9a024a..2a1d782fd727 100644
--- a/ext/session/php_session.h
+++ b/ext/session/php_session.h
@@ -183,6 +183,7 @@ typedef struct _php_ps_globals {
bool mod_user_is_open;
bool mod_user_uses_object_methods_as_handlers;
bool use_trans_sid; /* contains the INI value of whether to use trans-sid */
+ bool random_seeded;
} php_ps_globals;
typedef php_ps_globals zend_ps_globals;
diff --git a/ext/session/session.c b/ext/session/session.c
index 1fdfc5d1073f..1723acc4448c 100644
--- a/ext/session/session.c
+++ b/ext/session/session.c
@@ -394,6 +394,18 @@ static zend_long php_session_gc(bool immediate)
if ((PS(mod_data) || PS(mod_user_implemented))) {
/* Use probability-based GC if not forced and probability is configured */
if (!collect && PS(gc_probability) > 0) {
+ /* Seed lazily on first GC draw per process. */
+ if (UNEXPECTED(!PS(random_seeded))) {
+ php_random_uint128_t seed;
+ if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
+ seed = php_random_uint128_constant(
+ php_random_generate_fallback_seed(),
+ php_random_generate_fallback_seed()
+ );
+ }
+ php_random_pcgoneseq128xslrr64_seed128(PS(random).state, seed);
+ PS(random_seeded) = true;
+ }
collect = php_random_range(PS(random), 0, PS(gc_divisor) - 1) < PS(gc_probability);
}
@@ -2891,14 +2903,7 @@ static PHP_GINIT_FUNCTION(ps)
.algo = &php_random_algo_pcgoneseq128xslrr64,
.state = &ps_globals->random_state,
};
- php_random_uint128_t seed;
- if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
- seed = php_random_uint128_constant(
- php_random_generate_fallback_seed(),
- php_random_generate_fallback_seed()
- );
- }
- php_random_pcgoneseq128xslrr64_seed128(ps_globals->random.state, seed);
+ ps_globals->random_seeded = false;
}
static PHP_MINIT_FUNCTION(session)