@@ -135,6 +135,30 @@ static char * php_zip_make_relative_path(char *path, size_t path_len) /* {{{ */
135135# define CWD_STATE_ALLOC (l ) emalloc(l)
136136# define CWD_STATE_FREE (s ) efree(s)
137137
138+ /* {{{ php_zip_file_error
139+ Entry error code, plus its message when message is not NULL.
140+ zip_error_t and its accessors only exist since libzip 1.0. */
141+ static int php_zip_file_error (struct zip_file * zf , const char * * message )
142+ {
143+ #if LIBZIP_VERSION_MAJOR < 1
144+ int zep , syp ;
145+
146+ zip_file_error_get (zf , & zep , & syp );
147+ if (message ) {
148+ * message = zip_file_strerror (zf );
149+ }
150+ return zep ;
151+ #else
152+ zip_error_t * err = zip_file_get_error (zf );
153+
154+ if (message ) {
155+ * message = zip_error_strerror (err );
156+ }
157+ return zip_error_code_zip (err );
158+ #endif
159+ }
160+ /* }}} */
161+
138162/* {{{ php_zip_extract_file */
139163static int php_zip_extract_file (struct zip * za , char * dest , const char * file , size_t file_len , zip_int64_t idx )
140164{
@@ -268,7 +292,21 @@ static int php_zip_extract_file(struct zip * za, char *dest, const char *file, s
268292 n = 0 ;
269293
270294 while ((n = zip_fread (zf , b , sizeof (b ))) > 0 ) {
271- php_stream_write (stream , b , n );
295+ if (php_stream_write (stream , b , n ) != n ) {
296+ n = -1 ;
297+ break ;
298+ }
299+ }
300+
301+ if (n < 0 ) {
302+ const char * message ;
303+
304+ if (php_zip_file_error (zf , & message ) != ZIP_ER_OK ) {
305+ php_error_docref (NULL , E_WARNING , "Cannot extract \"%s\": \"%s\"" , file , message );
306+ }
307+ php_stream_close (stream );
308+ zip_fclose (zf );
309+ goto done ;
272310 }
273311
274312 if (stream -> wrapper -> wops -> stream_metadata ) {
@@ -279,7 +317,7 @@ static int php_zip_extract_file(struct zip * za, char *dest, const char *file, s
279317 }
280318
281319 php_stream_close (stream );
282- n = zip_fclose (zf );
320+ n = zip_fclose (zf ) == 0 ? 0 : -1 ;
283321
284322done :
285323 efree (fullpath );
@@ -2953,10 +2991,6 @@ static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
29532991 PHP_ZIP_STAT_INDEX (intern , index , flags , sb );
29542992 }
29552993
2956- if (sb .size < 1 ) {
2957- RETURN_EMPTY_STRING ();
2958- }
2959-
29602994 if (len < 1 ) {
29612995 len = sb .size ;
29622996 }
@@ -2971,8 +3005,40 @@ static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
29713005 }
29723006
29733007 buffer = zend_string_safe_alloc (1 , len , 0 , 0 );
2974- zip_int64_t n = zip_fread (zf , ZSTR_VAL (buffer ), ZSTR_LEN (buffer ));
2975- if (n < 1 ) {
3008+
3009+ /* zip_fread() may return short reads, a truncated entry must not pass for a complete one. */
3010+ zip_int64_t n = 0 ;
3011+ while ((zip_uint64_t )n < ZSTR_LEN (buffer )) {
3012+ zip_int64_t rd = zip_fread (zf , ZSTR_VAL (buffer ) + n , ZSTR_LEN (buffer ) - n );
3013+
3014+ if (rd < 0 ) {
3015+ n = -1 ;
3016+ break ;
3017+ }
3018+ if (rd == 0 ) {
3019+ break ;
3020+ }
3021+ n += rd ;
3022+ }
3023+
3024+ if (n >= 0 && (zip_uint64_t )n == sb .size ) {
3025+ /* The whole entry has been consumed, read past its last byte so that
3026+ * libzip reaches the end of the stream and validates the CRC. */
3027+ char tmp ;
3028+ if (zip_fread (zf , & tmp , 1 ) < 0 ) {
3029+ n = -1 ;
3030+ }
3031+ }
3032+ if (n < 0 ) {
3033+ const char * message ;
3034+
3035+ php_zip_file_error (zf , & message );
3036+ php_error_docref (NULL , E_WARNING , "Cannot read entry: %s" , message );
3037+ zip_fclose (zf );
3038+ zend_string_efree (buffer );
3039+ RETURN_FALSE ;
3040+ }
3041+ if (n == 0 ) {
29763042 zip_fclose (zf );
29773043 zend_string_efree (buffer );
29783044 RETURN_EMPTY_STRING ();
0 commit comments