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

nuid: use unsigned intermediate to avoid left shift undefined behavior #686

Merged
merged 1 commit into from
Oct 15, 2023
Merged

nuid: use unsigned intermediate to avoid left shift undefined behavior #686

merged 1 commit into from
Oct 15, 2023

Conversation

torque
Copy link
Contributor

@torque torque commented Oct 15, 2023

Trying to left shift any uint32_t value greater than 0x7FFFFFFF by 32 places as an int64_t is technically undefined behavior. If this is hit during runtime, clang's ubsan will report "runtime error: left shift of 2147483648 by 32 places cannot be represented in type 'int64_t' (aka 'long')". Since this is occurs in a random number generator, the behavior is nondeterministic as well, though it seems to happen quite often.

I ran into this frequently while working on wrapping nats.c with zig, which enables Clang's -fsanitize-trap=undefined flag by default in debug builds. There may be other similar undefined behavior lurking in some of the other bit shifts, but this is the only place I've seen it pop up in the debugger so far.

See https://godbolt.org/z/z6aabseET for a reduction of the issue.

Trying to shift any uint32_t value greater than 0x7FFFFFFF as an
int64_t is technically undefined behavior. If this is hit during
runtime, clang's ubsan will report "runtime error: left shift of
2147483648 by 32 places cannot be represented in type 'int64_t'
(aka 'long')". Since this is occurs in a random number generator, the
behavior is nondeterministic as well, though it seems to happen quite
often.
@levb levb self-requested a review October 15, 2023 14:02
@levb levb added this to the v3.7.1 milestone Oct 15, 2023
Copy link
Collaborator

@levb levb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for the contribution @torque !

@levb levb merged commit 7afcfa2 into nats-io:main Oct 15, 2023
1 check passed
@levb
Copy link
Collaborator

levb commented Oct 15, 2023

@torque would you please paste the compile command & flags here, I can maybe add them to our CI?

@torque
Copy link
Contributor Author

torque commented Oct 16, 2023

I think the most straightforward way to integrate this would be to add -fsanitize=undefined to your compilation CFLAGS for CI builds when running tests. I believe this flag is supported all the way back to clang 3.7 and gcc 5.1.

To elaborate, my understanding is that -fsanitize=undefined by itself will just print a message to stderr when a program encounters undefined behavior (as shown in the godbolt link above). If you additionally add -fsanitize-trap=undefined, it will cause the program to execute a trap instruction, immediately exiting the running program. This is useful because it directly indicates a program failure (via unexpected exit), rather than just passively logging, but it has the downside that it does not print out where the undefined behavior was encountered (you are expected to be running under a debugger to retrieve the stack trace, I think), so it may not be appropriate for a CI environment. Also, while -fsanitize-trap=undefined is supported back to clang 3.7, it looks like it was only added to gcc in gcc 13, which is pretty recent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants