Fix a typo that cause inconsistent hash between streaming and stateless way for XXH3 128-bit variant with custom secret and seed 0 #894
No reviewers
Labels
No labels
announcement
bug
build
CI
dependencies
documentation
duplicate
enhancement
experience feedback
feature request
github_actions
help wanted
install
invalid
Long-term
optimization
portability
question
target-specific
test issue
wontfix
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gh-Cyan4973/xxHash!894
Loading…
Reference in a new issue
No description provided.
Delete branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
I found that the streaming way returns different hash from
XXH3_128bits_withSecretandSeed()with seed0and a custom secret (notXXH3_kSecret) when the input length not greater thanXXH3_MIDSIZE_MAX, while the 64-bit variant works fine.I read the code and felt that there may be a typo that if there is also
if (state->useSeed)inXXH3_128bits_digest(), it will be fine too.I also added the test cases for that
_withSecretandSeedis the same as_withSeed()whenlen <= XXH3_MIDSIZE_MAXfor both 64-bit and 128-bit variant.Hi @hltj , thank you for the report and PR.
I also have confirmed your issue and this PR with the following code:
https://gist.github.com/t-mat/700503ca450a4e997e04af84b97fcb7f
XXH3_128bits_digest()has a bug.lengthis less than or equal toXXH3_MIDSIZE_MAX(240).XXH3_state_t::seedis0.note:
xxhsumis not affected by this issue. See cli/xxhsum.c : XSUM_hashStream()@Cyan4973 , @easyaspi314
Although this PR fixes the bug, it is a breaking change, technically.
I mean if this fix will be merged and released in the next release,
XXH3_128bits_digest()may return "different hash value" suddenly.So, I think we should think how we can/should mitigate it.
0.xthough.[[deprecated]]. And add new API (or API set) which works properly.Hi @t-mat , thank you so much for confirming, explaining the details as well as the fix/release suggestions.
The only I want to clarify that it is not
XXH3_128bits_withSecretandSeed()'s issue, butXXH3_128bits_digest's. And the change will not break the behavior ofXXH3_128bits_withSecretandSeed(), but it will break the behavior ofXXH3_128bits_digest()in the corner case.@hltj thanks! You're right. I've fixed my comment to prevent further misunderstanding.
Idea: Since secretAndSeed is the only thing broken afaik, we only need one API to wrap.
We have used opt-in macros when we did the internal API rename.
3. Repurpose
useSeedasstateTypewhere we can set a tag for the old API:Then in
XXH3_128bits_finalize, doAnd in
XXH3_64bits_finalizewe can dostate->stateType != XXH3_STATE_TYPE_SECRET.Make sure we make this very clear in documentation and comments.
This way, apps compiled against libxxhash.so won't change since they will use the old symbol, and defining that macro would make theoretical "old apps" that expect the broken hash values for some reason work.
Coming back on this topic,
after reading the proposed code change, and the comments,
it looks to me that this is a pretty clear case of detecting and fixing a bug.
XXH3_128bits_withSecretandSeed()has a very clear contract, which is btw the same asXXH3_64bits_withSecretandSeed():So deviating from this contract is a bug, clear and simple.
Beyond fixing the bug, what matters is to build an ability to detect such bug in the future, which this PR also provides.
So, as far as this PR is concerned, I think it's good to go.
The next question is probably: does this fix deserves a new release ? or even an urgent release ?
Well, I believe that the entry point
XXH3_128bits_withSecretandSeed(), which is the only one impacted, is not employed much out there. As far as I can tell, it's fairly niche, otherwise we would have received more bug reports of this kind.And it is likely going to remain so in the foreseeable future, since the primary motivation for this prototype was to reduce latency in the case where a
seedis employed and input size is >= 240 bytes forcing the creation of a custom secret at every hash (which only matters if input is fairly small, like ~1 KB range). That's a very specific scenario.Furthermore, since the bug is only active when
seed = 0, and this case essentially defeats the point of usingXXH3_128bits_withSecretandSeed()(just use regularXXH3_128bits()in this case).edit : and now that I'm analyzing the tests, I realize there are even more conditions:
secretmust be different from the one generated withXXH3_generateSecret_fromSeed()Given this pretty huge list of pre-conditions, I don't anticipate any real-world impact.
That being said, this bug definitely deserves a mention on the release page.
As a side note:
I was testing this capability,
and, sure it works fine for
sanity_test,but it does not for
xxhsum's internal sanity test (which is quicker / less extensive).That's because, even though these tools use almost the same code,
it's not the same code, meaning similar functions are just duplicated.
We may benefit from having a single source of truth for the common parts of the code base,
so that updates like this one benefit both targets.
Thanks @hltj , that's a great investigation and great fix !