Skip to content
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

fix issue 44108 #3033

Merged
merged 1 commit into from
Jan 27, 2022
Merged

fix issue 44108 #3033

merged 1 commit into from
Jan 27, 2022

Conversation

Cyan4973
Copy link
Contributor

@Cyan4973 Cyan4973 commented Jan 27, 2022

credit to oss-fuzz

In rare circumstances, the block-splitter might cut a new block at the exact beginning of a repcode.
In which case, since litlength=0, if the repcode expected 1+ literals in front, its signification changes.

Thankfully, this scenario is controlled in ZSTD_seqStore_resolveOffCodes(),
and the repcode is transformed into a raw offset when its new meaning is incorrect.

In more complex scenarios, the previous block might be emitted as uncompressed at a later decision stage,
thus modifying the expected repcode history.
In the specific case discovered by oss-fuzz, the first block is emitted as uncompressed,
so the repcode history remains at its starting default values: 1,4,8.

But since the starting repcode is repcode3, and the literal length is == 0,
its meaning is now : = repcode1 - 1.
Since repcode1==1, it results in an offset value of 0, which is invalid.

So that's what the assert() was verifying : the result of the repcode translation should be a valid offset.
And since it was not, it was firing.

But actually, it doesn't matter, because this result will then be compared to reality,
and since it's an invalid offset, it will necessarily be discarded if incorrect,
then the repcode will be replaced by a raw offset.

So the assert() is not useful.
Furthermore, it's incorrect, because it assumes this situation cannot happen, while it actually can happen, as just described in above scenario.

credit to oss-fuzz

In rare circumstances, the block-splitter might cut a block at the exact beginning of a repcode.
In which case, since litlength=0, if the repcode expected 1+ literals in front, its signification changes.
This scenario is controlled in ZSTD_seqStore_resolveOffCodes(),
and the repcode is transformed into a raw offset when its new meaning is incorrect.

In more complex scenarios, the previous block might be emitted as uncompressed after all,
thus modifying the expected repcode history.
In the case discovered by oss-fuzz, the first block is emitted as uncompressed,
so the repcode history remains at default values: 1,4,8.

But since the starting repcode is repcode3, and the literal length is == 0,
its meaning is : = repcode1 - 1.
Since repcode1==1, it results in an offset value of 0, which is invalid.

So that's what the `assert()` was verifying : the result of the repcode translation should be a valid offset.

But actually, it doesn't matter, because this result will then be compared to reality,
and since it's an invalid offset, it will necessarily be discarded if incorrect,
then the repcode will be replaced by a raw offset.

So the `assert()` is not useful.
Furthermore, it's incorrect, because it assumes this situation cannot happen, but it does, as described in above scenario.
@Cyan4973 Cyan4973 merged commit 5d70ec0 into dev Jan 27, 2022
@Cyan4973 Cyan4973 deleted the fix44108 branch January 13, 2023 04:28
@Cyan4973 Cyan4973 mentioned this pull request Feb 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants