Hacker News new | past | comments | ask | show | jobs | submit login

I don't think making it defined would help much. Overflowing a signed integer is a bug in logic. It would be ideal to have a crash on that. Continuing is going to be bad one way or another unless you luck out with your buggy code so the way the implementation works saves you. It can't be relied upon in general case though.

Imo the way is to develop more tools that detect (either by analysis or at runtime) those bugs and run the code with those attached as often as you can afford it (to take the performance penalty).




That's the thing. When C is portable assembler, you expect signed integer overflow to be the same as in assembler. On x86 you don't expect a trap.

There are quite a few idioms where overflow is used intentionally. There is no reason to turn that into undefined behavior if it works fine on the underlying platform.


There isn’t a “the same as assembler” that’d make sense in C.

For instance ARM, x86 scalar, and x86 SIMD all have different integer overflow rules for shift operations.


I don't expect C to be portable assembler. I expect it to be a simple fast language with clear rules. I am not sure what you mean by an idiom here. I suppose you mean assembler idiom as in C it was always a simple bug in logic. Obviously you can't just use idioms from one language in another without checking what the rules in the other language are.

Platform specific behavior should be as rare as possible. It's a recipe for bugs. The rule is simple enough and major compilers have flags to prevent the overflow. Obviously you pay the performance penalty for using them. It's a choice you're free to make as it should be.


"Can't be relied upon in general case" is implementation defined behavior, not undefined. UB is much worse than what you think, it's not merely can't be relied, but the program can launch nuclear rockets when it happens. Preventing such interpretations is very helpful.


I meant that you can't rely on platform/implementation specific behavior to save you. It's the worst of both worlds: you don't get performance benefits of UB and you introduce a disaster waiting to happen once your code runs on another platform or is compiled with another compiler.

I know what UB is. I think the idea is brilliant and saves millions of dollars of burnt coal every day. Sometimes security matters more and then you compile your code with every flag/sanitizer you can find to exchange performance for security.


Performance benefits of UB aren't obvious, and even if they existed, it's not obvious they are a good tradeoff. It's not only security, but any application mildly interested in correctness. Which leaves gamedev as the only consumer of aggressive UB optimization, and with the advent of online gaming even those might be more interested in security.


Overflowing a signed integer is not always a bug in logic, if you know the underlying representation then purposefully overflowing can be pretty useful.


According to this, it is a bug in the vast majority of cases (over 90%): http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p090...


The problem is that the vast majority of overflows are indeed logic errors. If the behaviour were defined, I wouldn't be able to use ubsan to catch them.


As was pointed out to me previously, UBSAN actually provides checking for unsigned overflow even though it's defined. So if signed overflow was defined, that would not stop UBSAN from flagging it.

Because in reality as you observe so many overflows are unintended. Most of the time programmers, especially dealing with 16-bit or wider integer types, treat them as though they were mathematical integers, and so overflow is extraordinary and worth flagging.

Unfortunately UBSAN doesn't prevent you getting this wrong, perhaps somewhere important. If your test inputs never trip the corner case where overflow occurs you can switch off UBSAN in release builds, ship it, and overflow on a real system where it has real consequences.


Well, TIL, it goes to show how rare are intentional overflows.


> the vast majority of overflows are indeed logic errors

This topic has turned up before. [0]

edit: I got the default behaviour the wrong way round here:

I think C# gets this right. Ordinarily it handles integer overflow by throwing an exception, but it has an unchecked keyword which gives you wrapping. [1] If you're writing code that is expected to wrap, you use the unchecked keyword, and you carry on using the usual arithmetic operators. (I believe you can also instruct the C# compiler to default to unchecked behaviour, so there's also a checked keyword. This strikes me as a mistake.)

Strictly speaking Java gives you the option of checked vs unchecked integer arithmetic, but with terrible ergonomics: the '+' operator always silently wraps, if you want throw-on-overflow behaviour you have to call a method. [2] This is of course so unsightly that Java programmers tend to stick with the infix arithmetic operators regardless of the wrapping behaviour.

C++ has templates and operator overloading, so you can use a library to get signed arithmetic to wrap, or throw, without undefined behaviour. [3] Such libraries are very rarely used, though.

See also this very good blog post by John Regehr, who specialises in this kind of thing. [4] To quote the post:

> Java-style wrapping integers should never be the default, this is arguably even worse than C and C++’s UB-on-overflow which at least permits an implementation to trap.

[0] https://news.ycombinator.com/item?id=26538606

[1] https://docs.microsoft.com/en-us/dotnet/csharp/language-refe...

[2] https://docs.oracle.com/en/java/javase/17/docs/api/java.base...

[3] https://www.boost.org/doc/libs/1_78_0/libs/safe_numerics/doc...

[4] https://blog.regehr.org/archives/1401


What if you write code that doesn't wrap but you don't want the exception or any kind of check either?

That's the whole point of UB. Let the compiler optimize by assuming your integers are in range. I get that it doesn't matter in a language like C#/Java (they are so slow additional checks don't register) but in C throwing has costs and code that wraps would require additional logic on some platforms.

One way or another if you want C to "throw" (trap) you can get that by using compiler flags.


"unchecked" is the default in C#, and "checked" is opt-in, except for compile-time expressions (and System.Decimal, which always throws on overflow).

You can tell the compiler to use "checked" by default for a given project, but it's fairly rare to see that in practice.

I wish it was the other way around, but I guess they didn't consider the overhead of "checked" acceptable as a default back in 1999; and now it's a back-compat issue.


It is being discussed to change the defaults on .NET 7.


C# 11, rather? I wouldn't expect them to change anything on bytecode level, since the choice between wraparound and overflow is always explicit there.

Do you recall where that discussion is taking place? I can't find a proposal to this effect in the usual GitHub repo.


Because the change is on the Visual Studio templates to set the checkbox enabled by default, no need to change the language for something it already supports.

It was mentioned on one of the regular YouTube videos with the team, but I am failing to find it now.


Thanks, I've edited that in.


So you're fine with throwing your hands up "its unreliable, its a bug that should never happen" when standard lacks a clear recipe how to check beforehand whether signed integer arithmetic will overflow? Everyone rolls their own, introducing even more bugs.


Well, I prefer to have standard tools to check or a way to compile so it traps on the overflow. Majors compilers provide that if you value security over performance and are not sure about correctness of your logic.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: