Merge branch 'PHP-8.4'

* PHP-8.4:
  Fix zlib support for large files
  Fix memory leak on overflow in _php_stream_scandir()
This commit is contained in:
Niels Dossche 2025-02-14 23:11:17 +01:00
commit b3dd5a4c86
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
2 changed files with 52 additions and 29 deletions

View file

@ -45,36 +45,57 @@ static void php_gziop_report_errors(php_stream *stream, size_t count, const char
static ssize_t php_gziop_read(php_stream *stream, char *buf, size_t count) static ssize_t php_gziop_read(php_stream *stream, char *buf, size_t count)
{ {
struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *) stream->abstract; struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *) stream->abstract;
int read; ssize_t total_read = 0;
/* XXX this needs to be looped for the case count > UINT_MAX */ /* Despite the count argument of gzread() being "unsigned int",
read = gzread(self->gz_file, buf, count); * the return value is "int". Error returns are values < 0, otherwise the count is returned.
* To properly distinguish error values from success value, we therefore need to cap at INT_MAX.
/* Notify user of error, like the standard file wrapper normally does (e.g. errno=13 on mandatory lock failure). */ */
if (UNEXPECTED(read < 0)) { do {
php_gziop_report_errors(stream, count, "Read"); unsigned int chunk_size = MIN(count, INT_MAX);
} int read = gzread(self->gz_file, buf, chunk_size);
count -= chunk_size;
if (gzeof(self->gz_file)) { if (gzeof(self->gz_file)) {
stream->eof = 1; stream->eof = 1;
} }
if (UNEXPECTED(read < 0)) {
php_gziop_report_errors(stream, count, "Read");
return read; return read;
} }
total_read += read;
buf += read;
} while (count > 0 && !stream->eof);
return total_read;
}
static ssize_t php_gziop_write(php_stream *stream, const char *buf, size_t count) static ssize_t php_gziop_write(php_stream *stream, const char *buf, size_t count)
{ {
struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *) stream->abstract; struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *) stream->abstract;
ssize_t total_written = 0;
/* XXX this needs to be looped for the case count > UINT_MAX */ /* Despite the count argument of gzread() being "unsigned int",
int written = gzwrite(self->gz_file, (char *) buf, count); * the return value is "int". Error returns are values < 0, otherwise the count is returned.
* To properly distinguish error values from success value, we therefore need to cap at INT_MAX.
*/
do {
unsigned int chunk_size = MIN(count, INT_MAX);
int written = gzwrite(self->gz_file, buf, chunk_size);
count -= chunk_size;
/* Notify user of error, like the standard file wrapper normally does (e.g. errno=13 on mandatory lock failure). */
if (UNEXPECTED(written < 0)) { if (UNEXPECTED(written < 0)) {
php_gziop_report_errors(stream, count, "Write"); php_gziop_report_errors(stream, count, "Write");
return written;
} }
return written; total_written += written;
buf += written;
} while (count > 0);
return total_written;
} }
static int php_gziop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs) static int php_gziop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)

View file

@ -2469,25 +2469,19 @@ PHPAPI int _php_stream_scandir(const char *dirname, zend_string **namelist[], in
vector_size = 10; vector_size = 10;
} else { } else {
if(vector_size*2 < vector_size) { if(vector_size*2 < vector_size) {
/* overflow */ goto overflow;
php_stream_closedir(stream);
efree(vector);
return -1;
} }
vector_size *= 2; vector_size *= 2;
} }
vector = (zend_string **) safe_erealloc(vector, vector_size, sizeof(char *), 0); vector = (zend_string **) safe_erealloc(vector, vector_size, sizeof(zend_string *), 0);
} }
vector[nfiles] = zend_string_init(sdp.d_name, strlen(sdp.d_name), 0); vector[nfiles] = zend_string_init(sdp.d_name, strlen(sdp.d_name), 0);
nfiles++; if(vector_size < 10 || nfiles + 1 == 0) {
if(vector_size < 10 || nfiles == 0) { goto overflow;
/* overflow */
php_stream_closedir(stream);
efree(vector);
return -1;
} }
nfiles++;
} }
php_stream_closedir(stream); php_stream_closedir(stream);
@ -2497,5 +2491,13 @@ PHPAPI int _php_stream_scandir(const char *dirname, zend_string **namelist[], in
qsort(*namelist, nfiles, sizeof(zend_string *), (int(*)(const void *, const void *))compare); qsort(*namelist, nfiles, sizeof(zend_string *), (int(*)(const void *, const void *))compare);
} }
return nfiles; return nfiles;
overflow:
php_stream_closedir(stream);
for (unsigned int i = 0; i < nfiles; i++) {
zend_string_efree(vector[i]);
}
efree(vector);
return -1;
} }
/* }}} */ /* }}} */