Skip to content

Conversation

edwardneal
Copy link
Contributor

@edwardneal edwardneal commented Mar 9, 2025

Contributes to #112017. Reimplements #112032. From the PR description:

aapt2 creates ZIP entries with a "version needed to extract" of 0.0, but sometimes writes these entries with deflate compression (which requires a "version needed to extract" of 2.0.)

When a ZipArchive is opened in Update mode and a new item is added, the central directory is written with the correct value for this field. However, the local file header for existing entries isn't rewritten (because it hasn't changed.) This leads to a mismatch between the local file header values and the central directory header values, which causes Android app builds to fail.

When an existing entry's "version needed to extract" is changed, we force its local file header to be rewritten.

There's a test to cover this. I've also added an entry to the packaged_resources.zip file from the issue and confirmed that the field matches between the local file header and the CD header for all compressed entries in the resultant file.

The second commit enables ZipArchive to perform in-place updates of ZIP entries with slightly malformed "extra data" fields. This field must consist of [header, data] pairs, where the header is four bytes long. This isn't always adhered to though - zipalign will pad the extra data field with zeroes to make sure that the compressed data which follows is aligned to an X byte boundary.

To tolerate this, we introduce the concept of "trailing extra data" - the part of the extra data field which can't be successfully parsed as a [header, data] pair. This is detected when parsing the central directory header (and the local file header), used when calculating the "extra data length" part of the relevant header, then written out after the parsed extra data.

@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Mar 9, 2025
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-io-compression
See info in area-owners.md if you want to be subscribed.

This data is generated by zipalign. It needs to be accounted for and preserved.
@carlossanlop
Copy link
Contributor

Feel free to mark it as ready to review when you're ready and ping me to give it a full review.

@carlossanlop carlossanlop self-assigned this Mar 12, 2025
@edwardneal edwardneal changed the title Force write of local file header when "version needed to extract" changes Reimplement PR 112032, preserve trailing extra data when updating ZIP files Mar 12, 2025
@edwardneal edwardneal marked this pull request as ready for review March 12, 2025 22:02
@edwardneal
Copy link
Contributor Author

Thanks @carlossanlop - I can see enough tests passing on enough platforms for review.

Besides the tests, I've rolled the latest build of System.IO.Compression into a local .NET 10 preview installation and published a simple .NET for Android project. I was able to reproduce the problem resolved by PR 112032 with this, but not the issue with ZIP alignment. To reproduce that, I ran zipalign against a ZIP file and tried to modify it.

@ericstj
Copy link
Member

ericstj commented Mar 31, 2025

@carlossanlop -- seems like 44c08b8 created some conflicts here that were a bit too involved for me to easily resolve -- can you work with @edwardneal to get those resolved and review this change, please?

@edwardneal
Copy link
Contributor Author

Thanks both. I've merged and verified that all's well in CI, and have managed to successfully build an Android .apk and .aab file using the new version. This is ready to review.

Copy link
Contributor

@carlossanlop carlossanlop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @edwardneal . Left some comments/questions for you to consider.

Prove functionality where there are 3, 4, 7, 8 and 15 bytes of padding. This covers zero, one and multiple well-formed extra fields, with and without subsequent trailing data.
@carlossanlop
Copy link
Contributor

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@carlossanlop
Copy link
Contributor

Just in case, I ran the outerloop tests. If nothing concerning shows up, I'll approve and merge this.

Converted to a clearer member which generates the combinations dynamically.
@carlossanlop carlossanlop merged commit f93aa8a into dotnet:main Apr 15, 2025
82 of 87 checks passed
@carlossanlop
Copy link
Contributor

cc @jonathanpeppers this is the fix for the zipalign issue.

@edwardneal edwardneal deleted the bug/issue-112017-ii branch April 15, 2025 03:32
@jonathanpeppers
Copy link
Member

I will test this here:

The most recent Maestro PR didn't have this one yet:

I can try it on the next Maestro PR.

@edwardneal
Copy link
Contributor Author

Thanks @jonathanpeppers. I tested this PR earlier, and re-tested it with the most recent preview, using a new Android project with the settings:

<AndroidUseAapt2>True</AndroidUseAapt2>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<AndroidSigningKeyStore>[keystore]</AndroidSigningKeyStore>
<AndroidSigningStorePass>[password]</AndroidSigningStorePass>
<AndroidSigningKeyPass>[password]</AndroidSigningKeyPass>
<AndroidKeyStore>true</AndroidKeyStore>
<AndroidSigningKeyAlias>apk</AndroidSigningKeyAlias>
<_AndroidUseLibZipSharp>false</_AndroidUseLibZipSharp>

I cleaned the project, then ran dotnet publish -f net10.0-android -bl -v:diag. The build failed and returned the output:

error ANDZA0000: 04-15 20:03:18.161  4356 15832 W zip     : WARNING: header mismatch
error ANDZA0000:                                                                    
error ANDZA0000: 04-15 20:03:18.164  4356 15832 W zip     : WARNING: header mismatch
error ANDZA0000:                                                                    
error ANDZA0000: 04-15 20:03:18.165  4356 15832 W zip     : WARNING: header mismatch
error ANDZA0000:                                                                    
error ANDZA0000: 04-15 20:03:18.165  4356 15832 W zip     : WARNING: header mismatch
error ANDZA0000:                                                                    

I've replaced .NET 10 Preview 3's System.IO.Compression.dll with the output from building dotnet/runtime, then cleaned the Android project again and re-ran dotnet publish ... with varying combinations of AndroidUseAapt2 = True/False and AndroidPackageFormat = aab/apk. The build completes without error, and when I use 7-Zip to test the archive, that test passes.

I couldn't force the zipalign step to fail, so had to test that independently - I just ran zipalign against an appropriate ZIP file, verified that it had the unusual padding in the extra data field, then used that as the initial baseline data for the new unit tests.

Are there any other tests I can run locally prior to the next Maestro PR?

@jonathanpeppers
Copy link
Member

I would have thought a Release build (or dotnet publish) with default settings would run the zipalign step. It runs any time AndroidPackageFormat=aab, which should be the default for Release mode.

Your description sounds like you tested it, but I can check a .binlog (https://aka.ms/binlog) if you have one?

@jonathanpeppers
Copy link
Member

This change hasn't shown up yet this morning:

I'll try again later, this is currently dotnet/runtime/main from ~April 11.

@jonathanpeppers
Copy link
Member

I just rebased this one, to test out this change:

@github-actions github-actions bot locked and limited conversation to collaborators May 18, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.IO.Compression community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants