@@ -144,6 +144,19 @@ static uint32_t phar_tar_checksum(const char *buf, size_t len) /* {{{ */
144144}
145145/* }}} */
146146
147+ static bool phar_tar_skip (php_stream * fp , uint32_t size , size_t totalsize )
148+ {
149+ zend_off_t offset = php_stream_tell (fp );
150+
151+ if (offset < 0 || (uint64_t ) size > (uint64_t ) ZEND_LONG_MAX
152+ || (size_t ) offset > totalsize || size > totalsize - (size_t ) offset ) {
153+ return false;
154+ }
155+
156+ return php_stream_seek (fp , size , SEEK_CUR ) == 0
157+ && php_stream_tell (fp ) == offset + (zend_off_t ) size ;
158+ }
159+
147160bool phar_is_tar (const char * buf , const char * fname ) /* {{{ */
148161{
149162 tar_header * header = (tar_header * ) buf ;
@@ -260,6 +273,8 @@ zend_result phar_parse_tarfile(
260273 char buf [512 ], * actual_alias = NULL , * p ;
261274 phar_entry_info entry = {0 };
262275 size_t pos = 0 , read , totalsize ;
276+ zend_off_t end ;
277+ php_stream_statbuf ssb ;
263278 tar_header * hdr ;
264279 uint32_t sum1 , sum2 , size , old ;
265280 phar_archive_data * myphar , * actual ;
@@ -270,9 +285,17 @@ zend_result phar_parse_tarfile(
270285 * error = NULL ;
271286 }
272287
273- php_stream_seek (fp , 0 , SEEK_END );
274- totalsize = php_stream_tell (fp );
275- php_stream_seek (fp , 0 , SEEK_SET );
288+ if ((php_stream_stat (fp , & ssb ) == 0 && ssb .sb .st_size > ZEND_LONG_MAX )
289+ || php_stream_seek (fp , 0 , SEEK_END ) != 0
290+ || (end = php_stream_tell (fp )) < 0
291+ || php_stream_seek (fp , 0 , SEEK_SET ) != 0 ) {
292+ if (error ) {
293+ spprintf (error , 4096 , "phar error: tar file \"%s\" exceeds the supported stream offset range" , fname );
294+ }
295+ php_stream_close (fp );
296+ return FAILURE ;
297+ }
298+ totalsize = (size_t ) end ;
276299 read = php_stream_read (fp , buf , sizeof (buf ));
277300
278301 if (read != sizeof (buf )) {
@@ -324,7 +347,8 @@ zend_result phar_parse_tarfile(
324347 }
325348 }
326349
327- if (!phar_tar_size (hdr -> size , sizeof (hdr -> size ), & size )) {
350+ if (!phar_tar_size (hdr -> size , sizeof (hdr -> size ), & size )
351+ || (uint64_t ) size + 511 > (uint64_t ) ZEND_LONG_MAX ) {
328352 if (error ) {
329353 spprintf (error , 4096 , "phar error: \"%s\" is a corrupted tar file (invalid entry size)" , fname );
330354 }
@@ -397,12 +421,17 @@ zend_result phar_parse_tarfile(
397421 goto bail ;
398422 }
399423 myphar -> sig_len = sig_len ;
400- php_stream_seek (fp , curloc + 512 , SEEK_SET );
424+ if (curloc < 0 || (size_t ) curloc > totalsize
425+ || totalsize - (size_t ) curloc < 512
426+ || php_stream_seek (fp , curloc + 512 , SEEK_SET ) != 0 ) {
427+ if (error ) {
428+ spprintf (error , 4096 , "phar error: \"%s\" is a corrupted tar file (truncated)" , fname );
429+ }
430+ goto bail ;
431+ }
401432 /* signature checked out, let's ensure this is the last file in the phar */
402433 if (((hdr -> typeflag == '\0' ) || (hdr -> typeflag == TAR_FILE )) && size > 0 ) {
403- /* this is not good enough - seek succeeds even on truncated tars */
404- php_stream_seek (fp , 512 , SEEK_CUR );
405- if ((uint32_t )php_stream_tell (fp ) > totalsize ) {
434+ if (!phar_tar_skip (fp , 512 , totalsize )) {
406435 if (error ) {
407436 spprintf (error , 4096 , "phar error: \"%s\" is a corrupted tar file (truncated)" , fname );
408437 }
@@ -470,9 +499,7 @@ zend_result phar_parse_tarfile(
470499 /* skip blank stuff */
471500 size = ((size + 511 )& ~511 ) - size ;
472501
473- /* this is not good enough - seek succeeds even on truncated tars */
474- php_stream_seek (fp , size , SEEK_CUR );
475- if ((uint32_t )php_stream_tell (fp ) > totalsize ) {
502+ if (!phar_tar_skip (fp , size , totalsize )) {
476503 zend_string_free (entry .filename );
477504 if (error ) {
478505 spprintf (error , 4096 , "phar error: \"%s\" is a corrupted tar file (truncated)" , fname );
@@ -663,9 +690,7 @@ zend_result phar_parse_tarfile(
663690
664691 if (phar_tar_type_has_data (hdr -> typeflag ) && size > 0 ) {
665692next :
666- /* this is not good enough - seek succeeds even on truncated tars */
667- php_stream_seek (fp , size , SEEK_CUR );
668- if ((uint32_t )php_stream_tell (fp ) > totalsize ) {
693+ if (!phar_tar_skip (fp , size , totalsize )) {
669694 if (error ) {
670695 spprintf (error , 4096 , "phar error: \"%s\" is a corrupted tar file (truncated)" , fname );
671696 }
0 commit comments