-
Notifications
You must be signed in to change notification settings - Fork 114
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
Signed integer overflow causes undefined behaviour #117
Comments
I have some good news. I was able to trick the compiler into treating signed overflow as wrapping using the following C code, without requiring typedef union {
signed s;
unsigned u;
} sint;
int count_up()
{
sint i;
i.s = 0;
while(i.s > -1) {
i.u += 1;
}
return i.s;
}
int count_up_ub()
{
int i;
i = 0;
while(i > -1) {
i += 1;
}
return i;
} I wonder if this technique could be used by the C code generator. |
Signed integer overflow in Rust is supposed to wrap
Not exactly. Integer overflow (both signed and unsigned) in Rust is
invalid. That is, if your code causes an integer overflow, then your code
is defective. Whether or not the language catches you doing this is up to
the compiler options. By default, in debug builds, overflow reliably causes
a panic. In release builds, the *behavior* is that overflow occurs, and you
get whatever you get. This just happens to be wrapping arithmetic, but this
is *not* a guarantee of the language or compiler. The behavior, even in
release builds, can be changed by enabling overflow checks.
If your code overflows, it is broken. You should never rely on overflow
wrapping. If you want wrapping behavior, then you should use the wrapping
operators:
i = i.overflowing_add(1);
See:
rust-lang/rfcs#359
http://huonw.github.io/blog/2016/04/myths-and-legends-about-integer-overflow-in-rust/
…On Sat, Jun 8, 2019 at 4:20 PM Serentty ***@***.***> wrote:
I have some good news. I was able to trick the compiler into treating
signed overflow as wrapping using the following C code, without requiring
-fwrapv.
typedef union {
signed s;
unsigned u;
} sint;
int count_up()
{
sint i;
i.s = 0;
while(i.s > -1) {
i.u += 1;
}
return i.s;
}
int count_up_ub()
{
int i;
i = 0;
while(i > -1) {
i += 1;
}
return i;
}
I wonder if this technique could be used by the C code generator.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#117?email_source=notifications&email_token=ADLILBDBAIJVGO4EVZRZW3LPZQ5CXA5CNFSM4HWIJRC2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXIAN5Q#issuecomment-500172534>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ADLILBCDB2HQSLWFJN7EHELPZQ5CXANCNFSM4HWIJRCQ>
.
|
I believe it is guaranteed to either panic or wrap around on overflow. Quoting the reference:
|
"It may be X or it may be Y" is not something you can rely on, though. If
you're writing a library and publishing it as a crate, you don't have
control over the compiler flags that are used. So you shouldn't rely on one
behavior or the other -- you should express the semantics you want, by
using the overflowing_xxx() operations. Yes, they're more verbose, but they
are precise.
…On Mon, Jun 10, 2019 at 11:50 AM bjorn3 ***@***.***> wrote:
This just happens to be wrapping arithmetic, but this is *not* a
guarantee of the language or compiler.
I believe it is guaranteed to either panic or wrap around on overflow.
Quoting the reference
<https://doc.rust-lang.org/reference/behavior-not-considered-unsafe.html>:
When the programmer has enabled debug_assert! assertions (for example, by
enabling a non-optimized build), implementations must insert dynamic checks
that panic on overflow. Other kinds of builds may result in panics or
silently wrapped values on overflow, at the implementation's discretion.
In the case of implicitly-wrapped overflow, implementations must provide
well-defined (even if still considered erroneous) results by using two's
complement overflow conventions.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#117?email_source=notifications&email_token=ADLILBHMYGPWWDJ5FAQJL2TPZ2O5TA5CNFSM4HWIJRC2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXK3HAA#issuecomment-500544384>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ADLILBCEN2C4V6LCJNT4PVDPZ2O5TANCNFSM4HWIJRCQ>
.
|
You can rely on the fact that it isn't UB. |
As far as I understand, overflow in Rust is defined to either panic or wrap. Which it does is not defined, but it is defined that it does one of those two, and which actually happens depends on compiler flags. What is not allowed by the language standard is for the compiler to assume that overflow will not occur, the way that the C language standard allows. This means that optimizations based on the assumption that overflow will not occur are not valid. |
We can solve this in two different ways:
|
While adding |
Signed integer overflow in Rust is supposed to wrap, except in debug builds where it panics to warn developers of possibly unintentional overflow. However, when the following code is compiled into C, the overflow is left as-is, leaving it at the mercy of the C compiler to be optimized in ways that aren't allowed in Rust.
When compiled with the reference implementation, this prints “Done”, in accordance with the defined behaviour for signed wrapping in Rust. However, when compiled into C and then compiled with GCC, it optimizes the entire main function away, and leaves just an infinite loop.
The text was updated successfully, but these errors were encountered: