@@ -129,6 +129,30 @@ static char * php_zip_make_relative_path(char *path, size_t path_len) /* {{{ */
129129# define CWD_STATE_ALLOC (l ) emalloc(l)
130130# define CWD_STATE_FREE (s ) efree(s)
131131
132+ /* {{{ php_zip_file_error
133+ Entry error code, plus its message when message is not NULL.
134+ zip_error_t and its accessors only exist since libzip 1.0. */
135+ static int php_zip_file_error (struct zip_file * zf , const char * * message )
136+ {
137+ #if LIBZIP_VERSION_MAJOR < 1
138+ int zep , syp ;
139+
140+ zip_file_error_get (zf , & zep , & syp );
141+ if (message ) {
142+ * message = zip_file_strerror (zf );
143+ }
144+ return zep ;
145+ #else
146+ zip_error_t * err = zip_file_get_error (zf );
147+
148+ if (message ) {
149+ * message = zip_error_strerror (err );
150+ }
151+ return zip_error_code_zip (err );
152+ #endif
153+ }
154+ /* }}} */
155+
132156/* {{{ php_zip_extract_file */
133157static int php_zip_extract_file (struct zip * za , char * dest , const char * file , size_t file_len , zip_int64_t idx )
134158{
@@ -262,7 +286,21 @@ static int php_zip_extract_file(struct zip * za, char *dest, const char *file, s
262286 n = 0 ;
263287
264288 while ((n = zip_fread (zf , b , sizeof (b ))) > 0 ) {
265- php_stream_write (stream , b , n );
289+ if (php_stream_write (stream , b , n ) != n ) {
290+ n = -1 ;
291+ break ;
292+ }
293+ }
294+
295+ if (n < 0 ) {
296+ const char * message ;
297+
298+ if (php_zip_file_error (zf , & message ) != ZIP_ER_OK ) {
299+ php_error_docref (NULL , E_WARNING , "Cannot extract \"%s\": \"%s\"" , file , message );
300+ }
301+ php_stream_close (stream );
302+ zip_fclose (zf );
303+ goto done ;
266304 }
267305
268306 if (stream -> wrapper -> wops -> stream_metadata ) {
@@ -273,7 +311,7 @@ static int php_zip_extract_file(struct zip * za, char *dest, const char *file, s
273311 }
274312
275313 php_stream_close (stream );
276- n = zip_fclose (zf );
314+ n = zip_fclose (zf ) == 0 ? 0 : -1 ;
277315
278316done :
279317 efree (fullpath );
@@ -2905,10 +2943,6 @@ static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
29052943 PHP_ZIP_STAT_INDEX (intern , index , flags , sb );
29062944 }
29072945
2908- if (sb .size < 1 ) {
2909- RETURN_EMPTY_STRING ();
2910- }
2911-
29122946 if (len < 1 ) {
29132947 len = sb .size ;
29142948 }
@@ -2923,8 +2957,40 @@ static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
29232957 }
29242958
29252959 buffer = zend_string_safe_alloc (1 , len , 0 , 0 );
2926- zip_int64_t n = zip_fread (zf , ZSTR_VAL (buffer ), ZSTR_LEN (buffer ));
2927- if (n < 1 ) {
2960+
2961+ /* zip_fread() may return short reads, a truncated entry must not pass for a complete one. */
2962+ zip_int64_t n = 0 ;
2963+ while ((zip_uint64_t )n < ZSTR_LEN (buffer )) {
2964+ zip_int64_t rd = zip_fread (zf , ZSTR_VAL (buffer ) + n , ZSTR_LEN (buffer ) - n );
2965+
2966+ if (rd < 0 ) {
2967+ n = -1 ;
2968+ break ;
2969+ }
2970+ if (rd == 0 ) {
2971+ break ;
2972+ }
2973+ n += rd ;
2974+ }
2975+
2976+ if (n >= 0 && (zip_uint64_t )n == sb .size ) {
2977+ /* The whole entry has been consumed, read past its last byte so that
2978+ * libzip reaches the end of the stream and validates the CRC. */
2979+ char tmp ;
2980+ if (zip_fread (zf , & tmp , 1 ) < 0 ) {
2981+ n = -1 ;
2982+ }
2983+ }
2984+ if (n < 0 ) {
2985+ const char * message ;
2986+
2987+ php_zip_file_error (zf , & message );
2988+ php_error_docref (NULL , E_WARNING , "Cannot read entry: %s" , message );
2989+ zip_fclose (zf );
2990+ zend_string_efree (buffer );
2991+ RETURN_FALSE ;
2992+ }
2993+ if (n == 0 ) {
29282994 zip_fclose (zf );
29292995 zend_string_efree (buffer );
29302996 RETURN_EMPTY_STRING ();
0 commit comments