But are you sure exiting is more terrible than blindly forging ahead with a known-wrong number?
It’s also basically impossible to establish invariants about a system for code that is run in signal handlers
Out of curiosity, what domain is that? I can’t think of many use case where silent wrapping wouldn’t likely cause problems down the road.
Can you imagine working on a complicated Excel document when you run into a 2038 bug and you lose all your work instead of just having one of your future dates calculated as being in the 1970s?
Or flying a plane and the system has to reboot midair instead of saying you're at -32768 feet of altitude?
Or there are now 2.15 billion virus definitions, so your entire security suite just stops running?
Or your Tesla stops keeping you in your lane without warning because your odometer turned over?
Most things are not Therac-25. Much more software is protecting us and our data than is exposing us to danger. Loss of data, loss of navigation, loss of safety systems or life support... simply unacceptable. Turn on -ftrapv when testing and especially when fuzzing, sure, but production code should almost always be built with -fwrapv.
If software crashes, it can simply be rebooted. I can't get the actual requirements for airplane control softward without paying hundreds of dollars. However, I would imagine that a reboot finish quicker than a pilot could open the manual to the "negative altitude indication" section.
This is exactly how Airbus's computer control philosophy works: Bail out in case of inconsistencies/unexpected errors. In the best case, this means falling back to the other (one or two) still operational redundant instances of the same component. The remediation is to literally reboot the errored-out component manually, mid-flight, with absolutely no adverse consequences. For all you know, this has already happened on a flight you've been on!
In the worst case (which can happen if it's an internally-consistent error condition, e.g. something like a conceptually unforeseen condition that none of the equivalent system instances can handle), the entire system declares failure and bails out to a lower-level system, if needed all the way down to mechanic/hydraulic connections to the most critical control surfaces in case of e.g. the A320. This can mean that e.g. the pilots need to start flying the plane manually.
Flying manually is preferred over e.g. one of two systems thinking that the plane is upside down, with a compromise action of rotating 90 degrees along some axis.
if your program loses data on a crash it is not worth using. What is this, 1970?
> Or flying a plane and the system has to reboot midair instead of saying you're at -32768 feet of altitude?
I'm pretty sure that's exactly what happens in fact: a quick reboot temporarily handling control to lower level or redundant controls is better than feeding wrong information to a sophisticated flight control system. I think this is generally true for all high reliability control systems.
Most of your examples would be just as bad if they started having unbounded incorrect behavior, and an overflow you didn't know about could lead to that. So don't get the math wrong!
AFAIK, over half of the mobile phones in the world, and probably nearly all of the fixed phones, do not run Swift, but instead some older language which does not have this "crash on overflow" behavior.
Note that an exception doesn't need to literally mean "the entire system comes to a screeching halt": You'd usually limit exception handling to the current operation.
You don't use trap representations to handle potential overflows. You check for possible overflow prior to the operation, and then handle appropriately.
Just like most things are not the Therac-25, most things are also not safety critical when they crash. Some web service that crashes just means that it returns 500 and you see it on whatever stability dashboard.
I have no idea what you mean by this. A couple types have diagnostics, but so do overflow and wrapping. Most don't. In general, it's much easier to have runtime diagnostics for arithmetic than for UB.
I guess if I squint and "silent" implies you're not allowed to install diagnostics, then it's worse? But that's so artificial of a comparison as to be silly.
Undefined behavior can transitively expand to the entire system very quickly. For any subsystem, the proper error handling for an overflow/arithmetic error that's not properly handled right in the place it occurs is to declare the subsystem inoperative, not to keep going.
If that approach routinely takes out too many and/or critical subsystems, you likely have much larger problems than integer overflows.
In that case, you can just detect a crash (eg with an external watchdog), and replace your process with one that runs fizzbuzz.
Fizzbuzz is wrong, but so would be continuing-no-matter-what-happened in general.
I worked at a company that sold a product that some customers used to make decisions involving a lot of money. A program misbehaved, or gave the wrong answer, or returned from a function too soon, or something. It didn't crash. A customer lost a lot of money, and we were liable.
It turned out that the bug was a symptom of undefined behavior that would have been caught by one of the flavors of assertions built into standard releases of the core C++ libraries. The team in charge of the app in question had long ago disabled those assertions because "they keep crashing the code."
Infrastructure within the company then went through a small internal crisis about whether to force assertions down people's throats or to let it be wrong on prod. The compromise was to have the assertions expand to loud warnings on prod. Ops then had a tool that would list all of the "your code is provably broken" instances, and that went to a dashboard used the shame the relevant engineering managers.
Not sure if it worked, but when you have a _lot_ of C++ code, a great deal of it is broken and "it's fine." Until it isn't.
The word "overflow".
> It’s also basically impossible to establish invariants about a system for code that is run in signal handlers
Signals are a pain but it doesn't specifically have to be a signal.
Consider protocols with wrapping sequence numbers. Pretty common in the wild. If I increment my 32-bit sequence counter and it goes from 2^32-1 back to 0, that's likely just expected behavior.
A normal exception without stack gremlins trying to break everything.