@@ -79,19 +79,103 @@ static bool php_zip_set_file_comment(struct zip *za, zip_uint64_t index, const c
7979}
8080/* }}} */
8181
82+ /* {{{ php_zip_comment_detach / php_zip_comment_restore
83+ * Modifying an entry (replacing its data, renaming it, changing its compression,
84+ * ...) makes libzip clone the central directory record of that entry, and the
85+ * clone shares the comment string with the original record. Up to libzip 1.11.4
86+ * zip_file_set_comment() then frees that shared string unconditionally, leaving
87+ * the original record with a dangling pointer: a use-after-free, followed by a
88+ * double free when the archive is closed.
89+ * Detaching the comment before the record gets cloned makes the clone own its
90+ * comment, so that libzip only ever frees a string of its own. Both helpers are
91+ * no-ops for entries without a comment, and they leave the error state of the
92+ * archive untouched. */
93+ static zend_string * php_zip_comment_detach (struct zip * za , zip_int64_t index )
94+ {
95+ zip_error_t * err ;
96+ int zip_err , sys_err ;
97+ zip_uint32_t comment_len = 0 ;
98+ const char * comment ;
99+ zend_string * saved ;
100+
101+ if (index < 0 ) {
102+ return NULL ;
103+ }
104+
105+ err = zip_get_error (za );
106+ zip_err = zip_error_code_zip (err );
107+ sys_err = zip_error_code_system (err );
108+
109+ comment = zip_file_get_comment (za , (zip_uint64_t ) index , & comment_len , ZIP_FL_ENC_RAW );
110+ if (comment == NULL || comment_len == 0 ) {
111+ saved = NULL ;
112+ } else {
113+ saved = zend_string_init (comment , comment_len , false);
114+ if (zip_file_set_comment (za , (zip_uint64_t ) index , NULL , 0 , 0 ) < 0 ) {
115+ zend_string_release_ex (saved , false);
116+ saved = NULL ;
117+ }
118+ }
119+
120+ zip_error_set (err , zip_err , sys_err );
121+
122+ return saved ;
123+ }
124+
125+ static zend_string * php_zip_comment_detach_name (struct zip * za , const char * name , zip_flags_t flags , zip_int64_t * index )
126+ {
127+ zip_error_t * err = zip_get_error (za );
128+ int zip_err = zip_error_code_zip (err );
129+ int sys_err = zip_error_code_system (err );
130+
131+ /* Only an overwritten entry can already be part of the archive. */
132+ * index = (flags & ZIP_FL_OVERWRITE ) ? zip_name_locate (za , name , flags ) : -1 ;
133+
134+ zip_error_set (err , zip_err , sys_err );
135+
136+ return php_zip_comment_detach (za , * index );
137+ }
138+
139+ static void php_zip_comment_restore (struct zip * za , zip_int64_t index , zend_string * comment )
140+ {
141+ zip_error_t * err ;
142+ int zip_err , sys_err ;
143+
144+ if (comment == NULL ) {
145+ return ;
146+ }
147+
148+ err = zip_get_error (za );
149+ zip_err = zip_error_code_zip (err );
150+ sys_err = zip_error_code_system (err );
151+
152+ zip_file_set_comment (za , (zip_uint64_t ) index , ZSTR_VAL (comment ), (zip_uint16_t ) ZSTR_LEN (comment ), 0 );
153+
154+ zip_error_set (err , zip_err , sys_err );
155+ zend_string_release_ex (comment , false);
156+ }
157+ /* }}} */
158+
82159# define add_ascii_assoc_string add_assoc_string
83160# define add_ascii_assoc_long add_assoc_long
84161
85162#ifdef HAVE_ENCRYPTION
86163static bool php_zip_file_set_encryption (struct zip * intern , zend_long index , zend_long method , char * password ) {
164+ zend_string * comment = php_zip_comment_detach (intern , (zip_int64_t )index );
165+ bool result ;
166+
87167 // FIXME: is a workaround to reset/free the password in case of consecutive calls.
88168 // when libzip 1.11.5 is available, we can save this call in this case.
89169 if (UNEXPECTED (zip_file_set_encryption (intern , (zip_uint64_t )index , ZIP_EM_NONE , NULL ) < 0 )) {
170+ php_zip_comment_restore (intern , (zip_int64_t )index , comment );
90171 php_error_docref (NULL , E_WARNING , "password reset failed" );
91172 return false;
92173 }
93174
94- return (zip_file_set_encryption (intern , (zip_uint64_t )index , (zip_uint16_t )method , password ) == 0 );
175+ result = (zip_file_set_encryption (intern , (zip_uint64_t )index , (zip_uint16_t )method , password ) == 0 );
176+ php_zip_comment_restore (intern , (zip_int64_t )index , comment );
177+
178+ return result ;
95179}
96180#endif
97181
@@ -298,6 +382,8 @@ static zend_result php_zip_add_file(ze_zip_object *obj, const char *filename, si
298382 struct zip_source * zs ;
299383 char resolved_path [MAXPATHLEN ];
300384 php_stream_statbuf ssb ;
385+ zend_string * comment ;
386+ zip_int64_t overwritten = -1 ;
301387
302388 if (ZIP_OPENBASEDIR_CHECKPATH (filename )) {
303389 return FAILURE ;
@@ -333,15 +419,24 @@ static zend_result php_zip_add_file(ze_zip_object *obj, const char *filename, si
333419 }
334420 /* Replace */
335421 if (replace >= 0 ) {
336- if (zip_file_replace (obj -> za , replace , zs , flags ) < 0 ) {
422+ int res ;
423+
424+ comment = php_zip_comment_detach (obj -> za , replace );
425+ res = zip_file_replace (obj -> za , replace , zs , flags );
426+ php_zip_comment_restore (obj -> za , replace , comment );
427+
428+ if (res < 0 ) {
337429 zip_source_free (zs );
338430 return FAILURE ;
339431 }
340432 zip_error_clear (obj -> za );
341433 return SUCCESS ;
342434 }
343435 /* Add */
436+ comment = php_zip_comment_detach_name (obj -> za , entry_name , flags , & overwritten );
437+
344438 obj -> last_id = zip_file_add (obj -> za , entry_name , zs , flags );
439+ php_zip_comment_restore (obj -> za , overwritten , comment );
345440 if (obj -> last_id < 0 ) {
346441 zip_source_free (zs );
347442 return FAILURE ;
@@ -1751,6 +1846,8 @@ PHP_METHOD(ZipArchive, addEmptyDir)
17511846 char * dirname ;
17521847 size_t dirname_len ;
17531848 char * s ;
1849+ zend_string * comment ;
1850+ zip_int64_t overwritten = -1 ;
17541851 zend_long flags = 0 ;
17551852
17561853 if (zend_parse_parameters (ZEND_NUM_ARGS (), "s|l" ,
@@ -1770,13 +1867,17 @@ PHP_METHOD(ZipArchive, addEmptyDir)
17701867 s = dirname ;
17711868 }
17721869
1870+ comment = php_zip_comment_detach_name (intern , (const char * )s , (zip_flags_t )flags , & overwritten );
1871+
17731872 if ((Z_ZIP_P (self )-> last_id = zip_dir_add (intern , (const char * )s , flags )) == -1 ) {
17741873 RETVAL_FALSE ;
17751874 } else {
17761875 zip_error_clear (intern );
17771876 RETVAL_TRUE ;
17781877 }
17791878
1879+ php_zip_comment_restore (intern , overwritten , comment );
1880+
17801881 if (s != dirname ) {
17811882 efree (s );
17821883 }
@@ -1983,6 +2084,8 @@ PHP_METHOD(ZipArchive, addFromString)
19832084 size_t name_len ;
19842085 ze_zip_object * ze_obj ;
19852086 struct zip_source * zs ;
2087+ zend_string * comment ;
2088+ zip_int64_t overwritten = -1 ;
19862089 zend_long flags = ZIP_FL_OVERWRITE ;
19872090
19882091 if (zend_parse_parameters (ZEND_NUM_ARGS (), "sS|l" ,
@@ -1999,7 +2102,10 @@ PHP_METHOD(ZipArchive, addFromString)
19992102 RETURN_FALSE ;
20002103 }
20012104
2105+ comment = php_zip_comment_detach_name (intern , name , (zip_flags_t )flags , & overwritten );
2106+
20022107 ze_obj -> last_id = zip_file_add (intern , name , zs , flags );
2108+ php_zip_comment_restore (intern , overwritten , comment );
20032109 if (ze_obj -> last_id == -1 ) {
20042110 zip_source_free (zs );
20052111 RETURN_FALSE ;
@@ -2260,6 +2366,8 @@ PHP_METHOD(ZipArchive, setExternalAttributesName)
22602366 char * name ;
22612367 zend_long flags = 0 , opsys , attr ;
22622368 zip_int64_t idx ;
2369+ zend_string * comment ;
2370+ bool result ;
22632371
22642372 if (zend_parse_parameters (ZEND_NUM_ARGS (), "sll|l" ,
22652373 & name , & name_len , & opsys , & attr , & flags ) == FAILURE ) {
@@ -2275,11 +2383,16 @@ PHP_METHOD(ZipArchive, setExternalAttributesName)
22752383
22762384 idx = zip_name_locate (intern , name , 0 );
22772385
2278- if (idx < 0 || zip_file_set_external_attributes (intern , idx , (zip_flags_t )flags ,
2279- (zip_uint8_t )(opsys & 0xff ), (zip_uint32_t )attr ) < 0 ) {
2386+ if (idx < 0 ) {
22802387 RETURN_FALSE ;
22812388 }
2282- RETURN_TRUE ;
2389+
2390+ comment = php_zip_comment_detach (intern , idx );
2391+ result = zip_file_set_external_attributes (intern , idx , (zip_flags_t )flags ,
2392+ (zip_uint8_t )(opsys & 0xff ), (zip_uint32_t )attr ) == 0 ;
2393+ php_zip_comment_restore (intern , idx , comment );
2394+
2395+ RETURN_BOOL (result );
22832396}
22842397/* }}} */
22852398
@@ -2290,6 +2403,8 @@ PHP_METHOD(ZipArchive, setExternalAttributesIndex)
22902403 zval * self = ZEND_THIS ;
22912404 zend_long index , flags = 0 , opsys , attr ;
22922405 struct zip_stat sb ;
2406+ zend_string * comment ;
2407+ bool result ;
22932408
22942409 if (zend_parse_parameters (ZEND_NUM_ARGS (), "lll|l" ,
22952410 & index , & opsys , & attr , & flags ) == FAILURE ) {
@@ -2299,8 +2414,13 @@ PHP_METHOD(ZipArchive, setExternalAttributesIndex)
22992414 ZIP_FROM_OBJECT (intern , self );
23002415
23012416 PHP_ZIP_STAT_INDEX (intern , index , 0 , sb );
2302- RETURN_BOOL (zip_file_set_external_attributes (intern , (zip_uint64_t )index ,
2303- (zip_flags_t )flags , (zip_uint8_t )(opsys & 0xff ), (zip_uint32_t )attr ) == 0 );
2417+
2418+ comment = php_zip_comment_detach (intern , (zip_int64_t )index );
2419+ result = zip_file_set_external_attributes (intern , (zip_uint64_t )index ,
2420+ (zip_flags_t )flags , (zip_uint8_t )(opsys & 0xff ), (zip_uint32_t )attr ) == 0 ;
2421+ php_zip_comment_restore (intern , (zip_int64_t )index , comment );
2422+
2423+ RETURN_BOOL (result );
23042424}
23052425/* }}} */
23062426
@@ -2490,6 +2610,8 @@ PHP_METHOD(ZipArchive, setCompressionName)
24902610 char * name ;
24912611 zip_int64_t idx ;
24922612 zend_long comp_method , comp_flags = 0 ;
2613+ zend_string * comment ;
2614+ bool result ;
24932615
24942616 if (zend_parse_parameters (ZEND_NUM_ARGS (), "sl|l" ,
24952617 & name , & name_len , & comp_method , & comp_flags ) == FAILURE ) {
@@ -2519,8 +2641,12 @@ PHP_METHOD(ZipArchive, setCompressionName)
25192641 RETURN_FALSE ;
25202642 }
25212643
2522- RETURN_BOOL (zip_set_file_compression (intern , (zip_uint64_t )idx ,
2523- (zip_int32_t )comp_method , (zip_uint32_t )comp_flags ) == 0 );
2644+ comment = php_zip_comment_detach (intern , idx );
2645+ result = zip_set_file_compression (intern , (zip_uint64_t )idx ,
2646+ (zip_int32_t )comp_method , (zip_uint32_t )comp_flags ) == 0 ;
2647+ php_zip_comment_restore (intern , idx , comment );
2648+
2649+ RETURN_BOOL (result );
25242650}
25252651/* }}} */
25262652
@@ -2531,6 +2657,8 @@ PHP_METHOD(ZipArchive, setCompressionIndex)
25312657 zval * this = ZEND_THIS ;
25322658 zend_long index ;
25332659 zend_long comp_method , comp_flags = 0 ;
2660+ zend_string * comment ;
2661+ bool result ;
25342662
25352663 if (zend_parse_parameters (ZEND_NUM_ARGS (), "ll|l" ,
25362664 & index , & comp_method , & comp_flags ) == FAILURE ) {
@@ -2554,8 +2682,12 @@ PHP_METHOD(ZipArchive, setCompressionIndex)
25542682
25552683 ZIP_FROM_OBJECT (intern , this );
25562684
2557- RETURN_BOOL (zip_set_file_compression (intern , (zip_uint64_t )index ,
2558- (zip_int32_t )comp_method , (zip_uint32_t )comp_flags ) == 0 );
2685+ comment = php_zip_comment_detach (intern , (zip_int64_t )index );
2686+ result = zip_set_file_compression (intern , (zip_uint64_t )index ,
2687+ (zip_int32_t )comp_method , (zip_uint32_t )comp_flags ) == 0 ;
2688+ php_zip_comment_restore (intern , (zip_int64_t )index , comment );
2689+
2690+ RETURN_BOOL (result );
25592691}
25602692/* }}} */
25612693
@@ -2568,6 +2700,8 @@ PHP_METHOD(ZipArchive, setMtimeName)
25682700 char * name ;
25692701 zip_int64_t idx ;
25702702 zend_long mtime , flags = 0 ;
2703+ zend_string * comment ;
2704+ bool result ;
25712705
25722706 if (zend_parse_parameters (ZEND_NUM_ARGS (), "sl|l" ,
25732707 & name , & name_len , & mtime , & flags ) == FAILURE ) {
@@ -2587,8 +2721,12 @@ PHP_METHOD(ZipArchive, setMtimeName)
25872721 RETURN_FALSE ;
25882722 }
25892723
2590- RETURN_BOOL (zip_file_set_mtime (intern , (zip_uint64_t )idx ,
2591- (time_t )mtime , (zip_uint32_t )flags ) == 0 );
2724+ comment = php_zip_comment_detach (intern , idx );
2725+ result = zip_file_set_mtime (intern , (zip_uint64_t )idx ,
2726+ (time_t )mtime , (zip_uint32_t )flags ) == 0 ;
2727+ php_zip_comment_restore (intern , idx , comment );
2728+
2729+ RETURN_BOOL (result );
25922730}
25932731/* }}} */
25942732
@@ -2599,6 +2737,8 @@ PHP_METHOD(ZipArchive, setMtimeIndex)
25992737 zval * this = ZEND_THIS ;
26002738 zend_long index ;
26012739 zend_long mtime , flags = 0 ;
2740+ zend_string * comment ;
2741+ bool result ;
26022742
26032743 if (zend_parse_parameters (ZEND_NUM_ARGS (), "ll|l" ,
26042744 & index , & mtime , & flags ) == FAILURE ) {
@@ -2607,8 +2747,12 @@ PHP_METHOD(ZipArchive, setMtimeIndex)
26072747
26082748 ZIP_FROM_OBJECT (intern , this );
26092749
2610- RETURN_BOOL (zip_file_set_mtime (intern , (zip_uint64_t )index ,
2611- (time_t )mtime , (zip_uint32_t )flags ) == 0 );
2750+ comment = php_zip_comment_detach (intern , (zip_int64_t )index );
2751+ result = zip_file_set_mtime (intern , (zip_uint64_t )index ,
2752+ (time_t )mtime , (zip_uint32_t )flags ) == 0 ;
2753+ php_zip_comment_restore (intern , (zip_int64_t )index , comment );
2754+
2755+ RETURN_BOOL (result );
26122756}
26132757/* }}} */
26142758
@@ -2666,6 +2810,8 @@ PHP_METHOD(ZipArchive, renameIndex)
26662810 char * new_name ;
26672811 size_t new_name_len ;
26682812 zend_long index ;
2813+ zend_string * comment ;
2814+ bool result ;
26692815
26702816 if (zend_parse_parameters (ZEND_NUM_ARGS (), "ls" , & index , & new_name , & new_name_len ) == FAILURE ) {
26712817 RETURN_THROWS ();
@@ -2682,7 +2828,11 @@ PHP_METHOD(ZipArchive, renameIndex)
26822828
26832829 ZIP_FROM_OBJECT (intern , self );
26842830
2685- RETURN_BOOL (zip_file_rename (intern , index , (const char * )new_name , 0 ) == 0 );
2831+ comment = php_zip_comment_detach (intern , (zip_int64_t )index );
2832+ result = zip_file_rename (intern , index , (const char * )new_name , 0 ) == 0 ;
2833+ php_zip_comment_restore (intern , (zip_int64_t )index , comment );
2834+
2835+ RETURN_BOOL (result );
26862836}
26872837/* }}} */
26882838
@@ -2694,6 +2844,8 @@ PHP_METHOD(ZipArchive, renameName)
26942844 struct zip_stat sb ;
26952845 char * name , * new_name ;
26962846 size_t name_len , new_name_len ;
2847+ zend_string * comment ;
2848+ bool result ;
26972849
26982850 if (zend_parse_parameters (ZEND_NUM_ARGS (), "ss" , & name , & name_len , & new_name , & new_name_len ) == FAILURE ) {
26992851 RETURN_THROWS ();
@@ -2708,7 +2860,11 @@ PHP_METHOD(ZipArchive, renameName)
27082860
27092861 PHP_ZIP_STAT_PATH (intern , name , name_len , 0 , sb );
27102862
2711- RETURN_BOOL (zip_file_rename (intern , sb .index , (const char * )new_name , 0 ) == 0 );
2863+ comment = php_zip_comment_detach (intern , (zip_int64_t )sb .index );
2864+ result = zip_file_rename (intern , sb .index , (const char * )new_name , 0 ) == 0 ;
2865+ php_zip_comment_restore (intern , (zip_int64_t )sb .index , comment );
2866+
2867+ RETURN_BOOL (result );
27122868}
27132869/* }}} */
27142870
0 commit comments