Use may_alias with unaligned reads to fix miscompilation on GCC #1013
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!1013
Loading…
Reference in a new issue
No description provided.
Delete branch "unaligned_read_may_alias"
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?
When
XXH_FORCE_MEMORY_ACCESS==1, which is the default on supported compilers unlike stated in the README, a strict aliasing violation occurs inXXH_read64, resulting in miscompilation on GCC (but not Clang) in some oddly specific circumstances.The following code reproduces the problem on x86_64 GCC 14.2.1 when compiled with -O3:
A bogus
-Wuninitializedwarning is produced if enabled, and the resulting program outputs an incorrect hash.While this definitely looks like a compiler bug in some ways, I see no reason to assume
aligned(1)alone should excempt the type from aliasing restrictions regardless, and addingmay_aliasdoes fix the problem.The separate strict aliasing bug when
XXH_FORCE_ALIGN_CHECK==1, discussed in #383, still remains.XXH64()exposes avoid*pointer as input argument,so it should be enough to allow aliasing.
However, with inlining via
XXH_INLINE_ALL, I guess the compiler can pass through this interface layer, and access directly the code below it, skipping thevoid*intermediate stage.I don't know C well enough to tell if the compiler is allow to do that (i.e. ignore
void*contract), or is well within its right.Either way, I can see how this can be an immediate problem.
The proposed fix is simple enough, and it should do exactly as advertised, i.e. allow aliasing, even when inlining.
That being said, this issue has not been exposed in CI so far, showing that we probably need to amend the test suite to produce the problem, observe its fix, and ensure the property remains guaranteed over time.
Since you also provide a test case in the issue description, I'll try to use that as a basis for the new test.
The compiler is allowed to do this. You have to cast back to the original type (or a compatible one) for it to be legal, i.e.
void*isn't a firewall for aliasing.Using
may_aliasis fine as a workaround, just not all compilers support it.I've been trying to reproduce the problem, by using exactly the example provided in this PR.
It's still not clear to me if this is a compilation issue (warning produced during compilation) or a runtime issue (strict aliasing violated at runtime, hopefully detectable with the UB sanitizer).
I tried both, but with no reproduction so far.
Here is my exact compilation line :
I've been using both gcc-9 and gcc-10.
The system I'm testing on doesn't have access to
gcc-14.It could be that this version of
gccis required to reproduce the issue.In which case, I will need to test this scenario on another system.
edit: another test with
mingw64, usinggcc-15.1compiler,still unable to reproduce the mentioned warning.
UBSAN doesn't detect aliasing problems, though it will detect unaligned access. Clang has a new sanitizer for this (
-fsanitize=type) but it's very primitive for now.I think the aliasing violation is clear here though, a
char*gets read asxxh_unalign64*.Finally, I could reproduce the warning using
gcc-13.3.0.Let's now try to create a new CI test based on this warning.
OK, we now have a failing test in #1047,
so if all goes according to expectations,
rebasing the failing test on top of this patch will fix the test.