Remix.run Logo
▲ nine_k 2 hours ago

I frankly find the situation around UB puzzling. AFAICT the idea was born in the times of very anemic compilers which would translate things that don't make definite sense, or work unpredictably on certain architectures. But why is it still a thing now, 50 years later?

If a compiler can detect UB, I think the only reasonable way for it is to have the program terminate immediately, not release "nasal demons". (And, of course, many bugs get smashed against the -Wall which should be the default.)

Of course, not all UB can be detected at compile time, like reading the padding bytes in a struct mentioned in the article. I wish there was a way to opt out of all UB, in an arbitrary but predictable way (an immediate crash would be fine), something like -fsanitize but for any UB at all that might happen at runtime. In quite some important code, I'd agree to tolerate the performance hit, it may be cheaper than dealing with the aftermath or an RCE exploited. I see that it's not entirely realistic though.

▲masklinn 2 hours ago | parent | next [-]

> If a compiler can detect UB

In the general case it can not, that is why they were made UBs in the first place.

And that is generally not how compilers see UB, they don’t look for UBs lying around, they assume UBs can’t happen and use that for constraints, which they then propagate.

For instance

    *i = 1
    if (id) { … }
The compiler will likely remove the check, because the dereference tags i as non-null, which makes the test redundant.

And this occurs and is extremely desirable every time e.g. a function is inlined in an other one which already checked for null.

▲nine_k an hour ago | parent [-]

But how is your example UB? It's straightforward data flow analysis.

AFAICT, UBI is stuff like that:

  short int i;
  for (i = 0; i < 33000; i++) do_something();
Whether this program continues as normal, crashes, or enters an infinite loop is platform-dependent, because short int can be as small as 16 bit, and an integer overflow past 32767 can be a crash or a wraparound.
▲vbezhenar 2 hours ago | parent | prev [-]

My understanding is that terminating program requires instructions and optimizing program involves removing "useless" instructions. If your code is littered with checks and jumps, it won't be faster than code without these checks and jumps.

In other words: if you write `a[i]` in C, the compiler could either add index checks if its knows the size of `a` with `abort()` calls; or it can just compile it to single access instruction. The latter is faster. The former might be useful for debug build, I guess, but otherwise it's too slow to be acceptable for C usage.

▲nine_k 2 hours ago | parent [-]

Yes. Carrying around stuff like ASan / TSan and a ton of other checks is just pretty expensive, and it does not even guarantee much. They only way is to use a better language %)