New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8193682: Infinite loop in ZipOutputStream.close() #5522
Conversation
|
@raviniitw2012 The following label will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command. |
Reviewers: @LanceAndersen @coffeys |
Webrevs
|
closed = true; | ||
out.close(); | ||
closed = true; | ||
throw ioe; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tried using try-finally instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Alan , Sorry for the delayed response , I was out of office for 3 weeks. I haven't tried with try-finally , I'm reworking on the fix as , if we directly use zip.finish() , we are facing the same issue. I will update here once the fix is ready.Thanks.
I have updated the review with the new fix. Instead of throwing an exception in close() method , Now when we get an exception during write inside deflate() , we will close the stream and throw the exception. Updated the test case in TestNG format. Mach5 : https://mach5.us.oracle.com/mdash/jobs/rreddy-jdk-20211011-1331-25193031/ |
@@ -249,7 +249,14 @@ public void close() throws IOException { | |||
protected void deflate() throws IOException { | |||
int len = def.deflate(buf, 0, buf.length); | |||
if (len > 0) { | |||
out.write(buf, 0, len); | |||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this use try with resources:
try (out) { ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the output stream is only closed if an exception is raised though ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we are closing the stream only when an exception occurs during a write operation
out.write(buf, 0, len); | ||
} catch (Exception e) { | ||
def.end(); | ||
out.close(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
out.close not needed with try with resources.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changes deflate to close the compressor and the output stream when there is an I/O exception. I expect this will need a spec change or a re-examination of the issue to see if there are alternatives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the out.close() call could be removed I guess. Leave it for user code to handle etc. Safer for spec also.
Main goal is to break the looping of the deflate call. The usesDefaultDeflater boolean might be useful in helping determine if def.end() should be called or not. If that boolean is false, then maybe we could just alter the input buffer by setting it to a size 0 buffer (ZipUtils.defaultBuf) -- worth a look.
out.write(buf, 0, len); | ||
try { | ||
out.write(buf, 0, len); | ||
} catch (Exception e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be a finally block?
private static byte[] b = new byte[FINISH_NUM]; | ||
private static Random rand = new Random(); | ||
|
||
@Test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can condense the test code to aid maintenance - please consider using DataProvider
Setting to "Request changes" until the spec impact is understood.
Hi , I have made the changes in fix , I think changing existing behavior in deflate() will need a spec change. Thanks, |
} catch(IOException e) { | ||
if (usesDefaultDeflater) | ||
def.end(); | ||
throw e; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The formatting is a bit wonky but I think this version is the best so far. It does mean that the stream state is undefined when close throws but this has always been the case.
I think overall this looks good. Thank you for continuing to work on this.
I do believe it would be worth adding a CSR just to document the behavior realizing that it was always left undefined in the past
Please also add a comment to each test describing its intent
|
||
public class GZipLoopTest { | ||
private static final int FINISH_NUM = 512; | ||
private static OutputStream outStream = new OutputStream() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a comment describing the intent of outstream and for FINISH_NUM. You might also consider a different name vs FINISH_NUM. Perhaps the comment will clarify this
zip.close(); | ||
} catch (IOException e) { | ||
//expected | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the above if an IOException is expected, should this be tested via assertThrows()?
|
||
@Test | ||
public void testGZipClose() throws IOException { | ||
rand.nextBytes(b); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could possibly consider using a BeforeTest or BeforeMethod if you choose to reduce redundancy. No biggie otherwise.
I think we are closer.
The formatting is better. Thank you for changing the name for the constant.
Please add comments describing the intent of the test, DataProvider, and BeforeTest method to make it clear to future maintainers when they look back on the tests.
We should still create a CSR to highlight the change.
Hello All . Thanks for reviewing these changes. I have created a CSR explaining the changes: https://bugs.openjdk.java.net/browse/JDK-8276305 |
/csr needed |
@raviniitw2012 this pull request will not be integrated until the CSR request JDK-8276305 for issue JDK-8193682 has been approved. |
…ipOutputStream.closeEntry() and DeflaterOutputStream.close()
Hi Ravi,
The current revision looks good to me.
Best
Lance
I agree, commit d911297 looks much better. There will be follow-up clarification needed to javadoc but they can be down with another JBS issue/PR. |
…se the deflater incase of IOException not in ZipException case scenario
Hi All, In the latest commit, I have resolved an issue in the case of ZipException in ZipOutputStream.closeEntry(). Thanks, |
@raviniitw2012 This change now passes all automated pre-integration checks. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 217 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. As you do not have Committer status in this project an existing Committer must agree to sponsor your change. Possible candidates are the reviewers of this PR (@AlanBateman, @LanceAndersen, @coffeys) but any other Committer may sponsor as well.
|
could you move the test up one directory to java/util/zip ? I don't think it's particular to GZIP any longer. Also perhaps a rename to something like CloseDeflaterTest etc.
…, moved testcase to util/zip/
/integrate |
@raviniitw2012 |
/sponsor |
Going to push as commit 1e9ed54.
Your commit was automatically rebased without conflicts. |
@coffeys @raviniitw2012 Pushed as commit 1e9ed54. |
Hi all,
Please review this fix for Infinite loop in ZipOutputStream.close().
The main issue here is when ever there is an exception during close operations on GZip we are not setting the deflator to a finished state which is leading to an infinite loop when we try writing on the same GZip instance( since we use while(!def.finished()) inside the write operation).
Thanks,
Ravi
Progress
Issues
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/5522/head:pull/5522
$ git checkout pull/5522
Update a local copy of the PR:
$ git checkout pull/5522
$ git pull https://git.openjdk.java.net/jdk pull/5522/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 5522
View PR using the GUI difftool:
$ git pr show -t 5522
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/5522.diff