@@ -41,19 +41,30 @@ static inline void phar_write_16(char buffer[2], uint32_t value)
4141# define PHAR_SET_32 (var , value ) phar_write_32(var, (uint32_t) (value));
4242# define PHAR_SET_16 (var , value ) phar_write_16(var, (uint16_t) (value));
4343
44- static int phar_zip_process_extra (php_stream * fp , phar_entry_info * entry , uint16_t len ) /* {{{ */
44+ static int phar_zip_process_extra (php_stream * fp , phar_entry_info * entry , uint16_t extra_len ) /* {{{ */
4545{
4646 union {
4747 phar_zip_extra_field_header header ;
4848 phar_zip_unix3 unix3 ;
4949 phar_zip_unix_time time ;
5050 } h ;
51+ size_t len = extra_len ;
5152 size_t read ;
5253
53- do {
54+ while (len ) {
55+ size_t header_size ;
56+
57+ if (len < sizeof (h .header )) {
58+ return FAILURE ;
59+ }
5460 if (sizeof (h .header ) != php_stream_read (fp , (char * ) & h .header , sizeof (h .header ))) {
5561 return FAILURE ;
5662 }
63+ len -= sizeof (h .header );
64+ header_size = PHAR_GET_16 (h .header .size );
65+ if (header_size > len ) {
66+ return FAILURE ;
67+ }
5768
5869 if (h .header .tag [0 ] == 'U' && h .header .tag [1 ] == 'T' ) {
5970 /* Unix timestamp header found.
@@ -62,7 +73,6 @@ static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, uint16
6273 * We only store the modification time in the entry, so only read that.
6374 */
6475 const size_t min_size = 5 ;
65- uint16_t header_size = PHAR_GET_16 (h .header .size );
6676 if (header_size >= min_size ) {
6777 read = php_stream_read (fp , & h .time .flags , min_size );
6878 if (read != min_size ) {
@@ -73,36 +83,48 @@ static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, uint16
7383 entry -> timestamp = PHAR_GET_32 (h .time .time );
7484 }
7585
76- len -= header_size + 4 ;
77-
7886 /* Consume remaining bytes */
79- if (header_size != read ) {
80- php_stream_seek ( fp , header_size - read , SEEK_CUR ) ;
87+ if (header_size != read && -1 == php_stream_seek ( fp , header_size - read , SEEK_CUR ) ) {
88+ return FAILURE ;
8189 }
90+ len -= header_size ;
8291 continue ;
8392 }
8493 /* Fallthrough to next if to skip header */
8594 }
8695
8796 if (h .header .tag [0 ] != 'n' || h .header .tag [1 ] != 'u' ) {
8897 /* skip to next header */
89- php_stream_seek (fp , PHAR_GET_16 (h .header .size ), SEEK_CUR );
90- len -= PHAR_GET_16 (h .header .size ) + 4 ;
98+ if (header_size && -1 == php_stream_seek (fp , header_size , SEEK_CUR )) {
99+ return FAILURE ;
100+ }
101+ len -= header_size ;
91102 continue ;
92103 }
93104
94105 /* unix3 header found */
95- read = php_stream_read (fp , (char * ) & (h .unix3 .crc32 ), sizeof (h .unix3 ) - sizeof (h .header ));
96- len -= read + 4 ;
106+ size_t unix3_size = sizeof (h .unix3 ) - sizeof (h .header );
107+ size_t field_size = header_size ;
108+ if (field_size == unix3_size - sizeof (h .unix3 .crc32 )) {
109+ /* Some archives omit the CRC32 from the unix3 size field. */
110+ field_size = unix3_size ;
111+ }
112+ if (field_size < unix3_size || field_size > len ) {
113+ return FAILURE ;
114+ }
97115
98- if (sizeof (h .unix3 ) - sizeof (h .header ) != read ) {
116+ read = php_stream_read (fp , (char * ) & (h .unix3 .crc32 ), unix3_size );
117+ if (unix3_size != read ) {
99118 return FAILURE ;
100119 }
101120
102- if (PHAR_GET_16 ( h . unix3 . size ) > sizeof ( h . unix3 ) - 4 ) {
121+ if (field_size > unix3_size ) {
103122 /* skip symlink filename - we may add this support in later */
104- php_stream_seek (fp , PHAR_GET_16 (h .unix3 .size ) - sizeof (h .unix3 .size ), SEEK_CUR );
123+ if (-1 == php_stream_seek (fp , field_size - unix3_size , SEEK_CUR )) {
124+ return FAILURE ;
125+ }
105126 }
127+ len -= field_size ;
106128
107129 /* set permissions */
108130 entry -> flags &= PHAR_ENT_COMPRESSION_MASK ;
@@ -113,7 +135,7 @@ static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, uint16
113135 entry -> flags |= PHAR_GET_16 (h .unix3 .perms ) & PHAR_ENT_PERM_MASK ;
114136 }
115137
116- } while ( len );
138+ }
117139
118140 return SUCCESS ;
119141}
0 commit comments