Skip to content
Open
12 changes: 11 additions & 1 deletion ext/phar/phar.c
Original file line number Diff line number Diff line change
Expand Up @@ -2819,7 +2819,17 @@ int phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defau
php_stream_flush(entry->cfp);
php_stream_filter_remove(filter, 1);
php_stream_seek(entry->cfp, 0, SEEK_END);
entry->compressed_filesize = ((uint32_t) php_stream_tell(entry->cfp)) - entry->header_offset;
if ((uint64_t) (php_stream_tell(entry->cfp) - entry->header_offset) > UINT32_MAX) {
if (must_close_old_file) {
php_stream_close(oldfile);
}
php_stream_close(newfile);
if (error) {
spprintf(error, 0, "unable to compress file \"%s\" to new phar \"%s\", compressed file is too large", entry->filename, phar->fname);
}
goto cleanup;
}
entry->compressed_filesize = php_stream_tell(entry->cfp) - entry->header_offset;
/* generate crc on compressed file */
entry->old_flags = entry->flags;
entry->is_modified = 1;
Expand Down
5 changes: 5 additions & 0 deletions ext/phar/phar_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,11 @@ struct _phar_archive_data {
uint32_t is_persistent:1;
};

static inline uint32_t phar_entry_max_size(const phar_archive_data *phar)
{
return phar->is_tar ? UINT32_MAX - 511 : UINT32_MAX;
}

typedef struct _phar_entry_fp_info {
enum phar_fp_type fp_type;
/* offset within fp of the file contents */
Expand Down
111 changes: 96 additions & 15 deletions ext/phar/phar_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -1640,6 +1640,34 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
return ZEND_HASH_APPLY_KEEP;
}

/* read filters change the length, so filtered streams are only checked after the copy */
if (!fp->readfilters.head && php_stream_stat(fp, &ssb) != -1
&& (int64_t) ssb.sb.st_size - php_stream_tell(fp) > (int64_t) phar_entry_max_size(phar_obj->archive)) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s is too large to be stored in a phar archive", str_key);

if (save) {
efree(save);
}

if (opened) {
zend_string_release_ex(opened, 0);
}

if (temp) {
efree(temp);
}

if (close_fp) {
php_stream_close(fp);
}

return ZEND_HASH_APPLY_STOP;
}

if (p_obj->fp == phar_obj->archive->ufp) {
php_stream_seek(p_obj->fp, 0, SEEK_END);
}

if (!(data = phar_get_or_create_entry_data(phar_obj->archive->fname, phar_obj->archive->fname_len, str_key, str_key_len, "w+b", 0, &error, 1))) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s cannot be created: %s", str_key, error);
efree(error);
Expand Down Expand Up @@ -1676,6 +1704,29 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
data->internal_file->offset_abs = data->internal_file->offset = php_stream_tell(p_obj->fp);
data->fp = NULL;
php_stream_copy_to_stream_ex(fp, p_obj->fp, PHP_STREAM_COPY_ALL, &contents_len);
if (contents_len > phar_entry_max_size(phar_obj->archive)) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s is too large to be stored in a phar archive", str_key);
data->internal_file->is_deleted = 1;
phar_entry_delref(data);

if (save) {
efree(save);
}

if (opened) {
zend_string_release_ex(opened, 0);
}

if (temp) {
efree(temp);
}

if (close_fp) {
php_stream_close(fp);
}

return ZEND_HASH_APPLY_STOP;
}
data->internal_file->uncompressed_filesize = data->internal_file->compressed_filesize =
php_stream_tell(p_obj->fp) - data->internal_file->offset;
if (php_stream_stat(fp, &ssb) != -1) {
Expand Down Expand Up @@ -1719,7 +1770,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
PHP_METHOD(Phar, buildFromDirectory)
{
char *error;
bool apply_reg = 0;
bool apply_reg = 0, pass_fp_is_new;
zval arg, arg2, iter, iteriter, regexiter;
struct _phar_t pass;
zend_string *dir, *regex = NULL;
Expand Down Expand Up @@ -1793,22 +1844,30 @@ PHP_METHOD(Phar, buildFromDirectory)
pass.base = dir;
pass.count = 0;
pass.ret = return_value;
pass.fp = php_stream_fopen_tmpfile();
if (pass.fp == NULL) {
zval_ptr_dtor(&iteriter);
if (apply_reg) {
zval_ptr_dtor(&regexiter);
pass.fp = phar_obj->archive->ufp;
pass_fp_is_new = pass.fp == NULL;
if (pass_fp_is_new) {
pass.fp = php_stream_fopen_tmpfile();
if (pass.fp == NULL) {
zval_ptr_dtor(&iteriter);
if (apply_reg) {
zval_ptr_dtor(&regexiter);
}
zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" unable to create temporary file", phar_obj->archive->fname);
RETURN_THROWS();
}
zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" unable to create temporary file", phar_obj->archive->fname);
RETURN_THROWS();
} else {
php_stream_seek(pass.fp, 0, SEEK_END);
}

if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
zval_ptr_dtor(&iteriter);
if (apply_reg) {
zval_ptr_dtor(&regexiter);
}
php_stream_close(pass.fp);
if (pass_fp_is_new) {
php_stream_close(pass.fp);
}
zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
RETURN_THROWS();
}
Expand All @@ -1833,7 +1892,7 @@ PHP_METHOD(Phar, buildFromDirectory)
if (apply_reg) {
zval_ptr_dtor(&regexiter);
}
php_stream_close(pass.fp);
phar_obj->archive->ufp = pass.fp;
}
}
/* }}} */
Expand Down Expand Up @@ -1878,10 +1937,15 @@ PHP_METHOD(Phar, buildFromIterator)
pass.base = base;
pass.ret = return_value;
pass.count = 0;
pass.fp = php_stream_fopen_tmpfile();
pass.fp = phar_obj->archive->ufp;
if (pass.fp == NULL) {
zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\": unable to create temporary file", phar_obj->archive->fname);
RETURN_THROWS();
pass.fp = php_stream_fopen_tmpfile();
if (pass.fp == NULL) {
zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\": unable to create temporary file", phar_obj->archive->fname);
RETURN_THROWS();
}
} else {
php_stream_seek(pass.fp, 0, SEEK_END);
}

if (SUCCESS == spl_iterator_apply(obj, (spl_iterator_apply_func_t) phar_build, (void *) &pass)) {
Expand All @@ -1892,7 +1956,7 @@ PHP_METHOD(Phar, buildFromIterator)
efree(error);
}
} else {
php_stream_close(pass.fp);
phar_obj->archive->ufp = pass.fp;
}
}
/* }}} */
Expand Down Expand Up @@ -3654,6 +3718,17 @@ static void phar_add_file(phar_archive_data **pphar, zend_string *file_name, con
}
#endif

if (zresource) {
php_stream_from_zval_no_verify(contents_file, zresource);
}

if ((content && ZSTR_LEN(content) > phar_entry_max_size(*pphar))
|| (contents_file && !contents_file->readfilters.head && php_stream_stat(contents_file, &ssb) != -1
&& (int64_t) ssb.sb.st_size - php_stream_tell(contents_file) > (int64_t) phar_entry_max_size(*pphar))) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s is too large to be stored in a phar archive", filename);
goto finish;
}

if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, filename, filename_len, "w+b", 0, &error, 1))) {
if (error) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s does not exist and cannot be created: %s", filename, error);
Expand All @@ -3678,13 +3753,19 @@ static void phar_add_file(phar_archive_data **pphar, zend_string *file_name, con
goto finish;
}
} else {
if (!(php_stream_from_zval_no_verify(contents_file, zresource))) {
if (!contents_file) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s could not be written to", filename);
phar_entry_delref(data);
goto finish;
}
php_stream_copy_to_stream_ex(contents_file, data->fp, PHP_STREAM_COPY_ALL, &contents_len);
}
if (contents_len > phar_entry_max_size(*pphar)) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s is too large to be stored in a phar archive", filename);
data->internal_file->is_deleted = 1;
phar_entry_delref(data);
goto finish;
}
data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len;
}

Expand Down
5 changes: 5 additions & 0 deletions ext/phar/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,11 @@ static ssize_t phar_stream_write(php_stream *stream, const char *buf, size_t cou
{
phar_entry_data *data = (phar_entry_data *) stream->abstract;

if ((uint64_t) data->position + count > phar_entry_max_size(data->phar)) {
php_error_docref(NULL, E_WARNING, "phar error: Could not write %d characters to \"%s\" in phar \"%s\", entry is too large", (int) count, data->internal_file->filename, data->phar->fname);
return -1;
}

php_stream_seek(data->fp, data->position + data->zero, SEEK_SET);
if (count != php_stream_write(data->fp, buf, count)) {
php_stream_wrapper_log_error(stream->wrapper, stream->flags, "phar error: Could not write %d characters to \"%s\" in phar \"%s\"", (int) count, data->internal_file->filename, data->phar->fname);
Expand Down
6 changes: 4 additions & 2 deletions ext/phar/tar.c
Original file line number Diff line number Diff line change
Expand Up @@ -830,9 +830,11 @@ static int phar_tar_writeheaders_int(phar_entry_info *entry, void *argument) /*

phar_tar_octal(header.mode, entry->flags & PHAR_ENT_PERM_MASK, sizeof(header.mode)-1);

if (FAILURE == phar_tar_octal(header.size, entry->uncompressed_filesize, sizeof(header.size)-1)) {
/* the reader rejects sizes above this limit, as they overflow the 512 byte padding */
if (entry->uncompressed_filesize > phar_entry_max_size(entry->phar)
|| FAILURE == phar_tar_octal(header.size, entry->uncompressed_filesize, sizeof(header.size)-1)) {
if (fp->error) {
spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too large for tar file format", entry->phar->fname, entry->filename);
spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename);
}
return ZEND_HASH_APPLY_STOP;
}
Expand Down
Loading
Loading