Recognize and ignore 0 bytes added by Android ZipAlign tool.
Note: modifying APKs with libzip will not keep alignments.
diff --git a/lib/zip_extra_field.c b/lib/zip_extra_field.c
index eb69356..c3d7387 100644
--- a/lib/zip_extra_field.c
+++ b/lib/zip_extra_field.c
@@ -205,8 +205,8 @@
}
-zip_extra_field_t *
-_zip_ef_parse(const zip_uint8_t *data, zip_uint16_t len, zip_flags_t flags, zip_error_t *error)
+bool
+_zip_ef_parse(const zip_uint8_t *data, zip_uint16_t len, zip_flags_t flags, zip_extra_field_t **ef_head_p, zip_error_t *error)
{
zip_buffer_t *buffer;
zip_extra_field_t *ef, *ef2, *ef_head;
@@ -218,7 +218,7 @@
ef_head = ef = NULL;
- while (_zip_buffer_ok(buffer) && !_zip_buffer_eof(buffer)) {
+ while (_zip_buffer_ok(buffer) && _zip_buffer_left(buffer) >= 4) {
zip_uint16_t fid, flen;
zip_uint8_t *ef_data;
@@ -227,14 +227,17 @@
ef_data = _zip_buffer_get(buffer, flen);
if (ef_data == NULL) {
- break;
+ zip_error_set(error, ZIP_ER_INCONS, 0);
+ _zip_buffer_free(buffer);
+ _zip_ef_free(ef_head);
+ return false;
}
if ((ef2=_zip_ef_new(fid, flen, ef_data, flags)) == NULL) {
zip_error_set(error, ZIP_ER_MEMORY, 0);
_zip_buffer_free(buffer);
_zip_ef_free(ef_head);
- return NULL;
+ return false;
}
if (ef_head) {
@@ -246,15 +249,26 @@
}
if (!_zip_buffer_eof(buffer)) {
- zip_error_set(error, ZIP_ER_INCONS, 0);
- _zip_buffer_free(buffer);
- _zip_ef_free(ef_head);
- return NULL;
+ /* Android APK files align stored file data with padding in extra fields; ignore. */
+ /* see https://android.googlesource.com/platform/build/+/master/tools/zipalign/ZipAlign.cpp */
+ size_t glen = _zip_buffer_left(buffer);
+ zip_uint8_t *garbage;
+ garbage = _zip_buffer_get(buffer, len);
+ if (glen >= 4 || garbage == NULL || memcmp(garbage, "\0\0\0", glen) != 0) {
+ zip_error_set(error, ZIP_ER_INCONS, 0);
+ _zip_buffer_free(buffer);
+ _zip_ef_free(ef_head);
+ return false;
+ }
}
_zip_buffer_free(buffer);
+
+ if (ef_head_p) {
+ *ef_head_p = ef_head;
+ }
- return ef_head;
+ return true;
}
@@ -398,14 +412,16 @@
if (ef_raw == NULL)
return -1;
- if ((ef=_zip_ef_parse(ef_raw, ef_len, ZIP_EF_LOCAL, &za->error)) == NULL) {
+ if (!_zip_ef_parse(ef_raw, ef_len, ZIP_EF_LOCAL, &ef, &za->error)) {
free(ef_raw);
return -1;
}
free(ef_raw);
-
- ef = _zip_ef_remove_internal(ef);
- e->orig->extra_fields = _zip_ef_merge(e->orig->extra_fields, ef);
+
+ if (ef) {
+ ef = _zip_ef_remove_internal(ef);
+ e->orig->extra_fields = _zip_ef_merge(e->orig->extra_fields, ef);
+ }
}
e->orig->local_extra_fields_read = 1;