Close the JarFile when JarAnalyzer's constructor fails - #166
Open
kratos0718 wants to merge 1 commit into
Open
Conversation
The constructor opened the JarFile and then listed and sorted its entries before reading the manifest. Only the manifest read was guarded: if listing or sorting threw, the constructor exited with the JarFile still open, and the caller had no reference on which to call closeQuietly(), so the handle leaked. Comparator.comparing(ZipEntry::getName) is one way in - it throws a NullPointerException on an entry with a null name, which a malformed archive can carry. Wrap everything between opening the file and assigning jarData, and release the handle on any IOException or RuntimeException before rethrowing. This also folds in the existing manifest cleanup, so there is a single exit path rather than two. Fixes apache#139
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #139
Problem
JarAnalyzer's constructor opens theJarFile, then lists and sorts its entries, and only afterwards reads the manifest:Only the manifest read is guarded. If the listing or the sort throws, the constructor exits with the
JarFilestill open — and because construction failed, the caller has no instance on which to callcloseQuietly(), so the handle leaks. The class javadoc already promises the file "will be closed if this occurs".Comparator.comparing(ZipEntry::getName)is one way in: it throwsNullPointerExceptionon an entry with a null name, which a malformed archive can carry.Fix
Wrap everything between opening the file and assigning
jarData, and release the handle on anyIOExceptionorRuntimeExceptionbefore rethrowing. That covers the sort, the entry listing, and anything else added to this region later, rather than enumerating individual failure modes.It also folds in the manifest cleanup that was already there, so there is one exit path instead of two — net effect is +11/-9 in a single method.
Note on #148
This is adjacent to your #148, which removes the
ZipExceptionre-wrap in the block immediately above. The two are semantically independent — that one changes how the open fails, this one changes what happens after a successful open — so they compose cleanly. Happy to rebase on top of it if it lands first, or to fold both into one change if you'd prefer.Testing
mvn test— 78 tests across the module, all passing.I did not add a test for the leak itself. Reproducing it needs an entry with a null name, which standard tooling won't produce, and asserting the descriptor was released needs the
JarFileto be injectable — the class constructs it internally. On POSIX a "can the file be deleted afterwards" assertion passes whether or not the handle leaked, so it would be a test that only means something on Windows. I'd rather not add a misleading one, but if you want a specific approach here I'm happy to write it.