Fix GH-18642: Signed integer overflow in ext/phar fseek

The overflow checking code already existed, but didn't work because the
math was done on signed numbers instead of unsigned numbers.
In the process I also discovered a pre-existing issue that needs to be
fixed (and seems that other stream wrappers can have this issue too).

Closes GH-18644.
This commit is contained in:
Niels Dossche 2025-05-24 17:37:22 +02:00
parent fab0a6d75c
commit 61884c3b52
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
3 changed files with 41 additions and 9 deletions

1
NEWS
View file

@ -10,6 +10,7 @@ PHP NEWS
- Phar:
. Add missing filter cleanups on phar failure. (nielsdos)
. Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos)
- Readline:
. Fix memory leak when calloc() fails in php_readline_completion_cb().

View file

@ -405,7 +405,7 @@ static int phar_stream_seek(php_stream *stream, zend_off_t offset, int whence, z
phar_entry_data *data = (phar_entry_data *)stream->abstract;
phar_entry_info *entry;
int res;
zend_off_t temp;
zend_ulong temp;
if (data->internal_file->link) {
entry = phar_get_link_source(data->internal_file);
@ -415,26 +415,28 @@ static int phar_stream_seek(php_stream *stream, zend_off_t offset, int whence, z
switch (whence) {
case SEEK_END :
temp = data->zero + entry->uncompressed_filesize + offset;
temp = (zend_ulong) data->zero + (zend_ulong) entry->uncompressed_filesize + (zend_ulong) offset;
break;
case SEEK_CUR :
temp = data->zero + data->position + offset;
temp = (zend_ulong) data->zero + (zend_ulong) data->position + (zend_ulong) offset;
break;
case SEEK_SET :
temp = data->zero + offset;
temp = (zend_ulong) data->zero + (zend_ulong) offset;
break;
default:
temp = 0;
}
if (temp > data->zero + (zend_off_t) entry->uncompressed_filesize) {
*newoffset = -1;
zend_off_t temp_signed = (zend_off_t) temp;
if (temp_signed > data->zero + (zend_off_t) entry->uncompressed_filesize) {
*newoffset = -1; /* FIXME: this will invalidate the ZEND_ASSERT(stream->position >= 0); assertion in streams.c */
return -1;
}
if (temp < data->zero) {
*newoffset = -1;
if (temp_signed < data->zero) {
*newoffset = -1; /* FIXME: this will invalidate the ZEND_ASSERT(stream->position >= 0); assertion in streams.c */
return -1;
}
res = php_stream_seek(data->fp, temp, SEEK_SET);
res = php_stream_seek(data->fp, temp_signed, SEEK_SET);
*newoffset = php_stream_tell(data->fp) - data->zero;
data->position = *newoffset;
return res;

View file

@ -0,0 +1,29 @@
--TEST--
GH-18642 (Signed integer overflow in ext/phar fseek)
--EXTENSIONS--
phar
--INI--
phar.require_hash=0
--FILE--
<?php
require_once __DIR__ . '/files/phar_oo_test.inc';
class MyFile extends SplFileObject
{
}
$phar = new Phar($fname);
$phar->setInfoClass('MyFile');
$f = $phar['a.php'];
var_dump($f->fseek(PHP_INT_MAX));
var_dump($f->fseek(0));
var_dump($f->fseek(PHP_INT_MIN, SEEK_END));
var_dump($f->fseek(0, SEEK_SET));
var_dump($f->fseek(1, SEEK_CUR));
var_dump($f->fseek(PHP_INT_MAX, SEEK_CUR));
?>
--EXPECT--
int(-1)
int(0)
int(-1)
int(0)
int(0)
int(-1)