@@ -144,6 +144,22 @@ 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
152+ #if SIZEOF_ZEND_LONG == 4
153+ || size > ZEND_LONG_MAX
154+ #endif
155+ || (size_t ) offset > totalsize || size > totalsize - (size_t ) offset ) {
156+ return false;
157+ }
158+
159+ return php_stream_seek (fp , size , SEEK_CUR ) == 0
160+ && php_stream_tell (fp ) == offset + (zend_off_t ) size ;
161+ }
162+
147163bool phar_is_tar (const char * buf , const char * fname ) /* {{{ */
148164{
149165 tar_header * header = (tar_header * ) buf ;
@@ -260,6 +276,8 @@ zend_result phar_parse_tarfile(
260276 char buf [512 ], * actual_alias = NULL , * p ;
261277 phar_entry_info entry = {0 };
262278 size_t pos = 0 , read , totalsize ;
279+ zend_off_t end ;
280+ php_stream_statbuf ssb ;
263281 tar_header * hdr ;
264282 uint32_t sum1 , sum2 , size , old ;
265283 phar_archive_data * myphar , * actual ;
@@ -270,9 +288,17 @@ zend_result phar_parse_tarfile(
270288 * error = NULL ;
271289 }
272290
273- php_stream_seek (fp , 0 , SEEK_END );
274- totalsize = php_stream_tell (fp );
275- php_stream_seek (fp , 0 , SEEK_SET );
291+ if ((php_stream_stat (fp , & ssb ) == 0 && ssb .sb .st_size > ZEND_LONG_MAX )
292+ || php_stream_seek (fp , 0 , SEEK_END ) != 0
293+ || (end = php_stream_tell (fp )) < 0
294+ || php_stream_seek (fp , 0 , SEEK_SET ) != 0 ) {
295+ if (error ) {
296+ spprintf (error , 4096 , "phar error: tar file \"%s\" exceeds the supported stream offset range" , fname );
297+ }
298+ php_stream_close (fp );
299+ return FAILURE ;
300+ }
301+ totalsize = (size_t ) end ;
276302 read = php_stream_read (fp , buf , sizeof (buf ));
277303
278304 if (read != sizeof (buf )) {
@@ -324,7 +350,8 @@ zend_result phar_parse_tarfile(
324350 }
325351 }
326352
327- if (!phar_tar_size (hdr -> size , sizeof (hdr -> size ), & size )) {
353+ if (!phar_tar_size (hdr -> size , sizeof (hdr -> size ), & size )
354+ || (uint64_t ) size + 511 > (uint64_t ) ZEND_LONG_MAX ) {
328355 if (error ) {
329356 spprintf (error , 4096 , "phar error: \"%s\" is a corrupted tar file (invalid entry size)" , fname );
330357 }
@@ -397,12 +424,17 @@ zend_result phar_parse_tarfile(
397424 goto bail ;
398425 }
399426 myphar -> sig_len = sig_len ;
400- php_stream_seek (fp , curloc + 512 , SEEK_SET );
427+ if (curloc < 0 || (size_t ) curloc > totalsize
428+ || totalsize - (size_t ) curloc < 512
429+ || php_stream_seek (fp , curloc + 512 , SEEK_SET ) != 0 ) {
430+ if (error ) {
431+ spprintf (error , 4096 , "phar error: \"%s\" is a corrupted tar file (truncated)" , fname );
432+ }
433+ goto bail ;
434+ }
401435 /* signature checked out, let's ensure this is the last file in the phar */
402436 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 ) {
437+ if (!phar_tar_skip (fp , 512 , totalsize )) {
406438 if (error ) {
407439 spprintf (error , 4096 , "phar error: \"%s\" is a corrupted tar file (truncated)" , fname );
408440 }
@@ -470,9 +502,7 @@ zend_result phar_parse_tarfile(
470502 /* skip blank stuff */
471503 size = ((size + 511 )& ~511 ) - size ;
472504
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 ) {
505+ if (!phar_tar_skip (fp , size , totalsize )) {
476506 zend_string_free (entry .filename );
477507 if (error ) {
478508 spprintf (error , 4096 , "phar error: \"%s\" is a corrupted tar file (truncated)" , fname );
@@ -663,9 +693,7 @@ zend_result phar_parse_tarfile(
663693
664694 if (phar_tar_type_has_data (hdr -> typeflag ) && size > 0 ) {
665695next :
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 ) {
696+ if (!phar_tar_skip (fp , size , totalsize )) {
669697 if (error ) {
670698 spprintf (error , 4096 , "phar error: \"%s\" is a corrupted tar file (truncated)" , fname );
671699 }
0 commit comments