
Undefined behavior can result in time travel (2014) - pcr910303
https://devblogs.microsoft.com/oldnewthing/20140627-00/?p=633
======
logicchop
I used to work on lower level stuff a place with a large C++ codebase designed
by a bunch of C guys. We had tight memory requirements, so a custom operator
new (not nothrow) was supplied; but exceptions were also disabled, so the
supplier of operator new couldn't throw in their implementation.

So that's how it was, and the code base was littered with lots of little
checks on what new expressions were returning, things like:

    
    
      int *p = new int[1024];
      if (!p) return SOME_ERROR;
      doSomething(*p);
    

Of course, the C++ standard says that operator new will never return null
(it's supposed to throw if it fails). So the compiler sees this code and says
"hey, that if (!p) check isn't necessary.." and so none of those little checks
gets emitted. Sure enough, it happens that the operator new returns null now
and then because everyone is on a tight memory budget, but instead of catching
it and returning an error, the code just goes on to crash.

~~~
MaxBarraclough
The answer seems to be to use std::nothrow, which looks like: [0]

    
    
      int *p = new (std::nothrow) int[5]; // gives null if alloc fails
    

A related question: Many C++ coding standards, including Google's, prohibit
use of C++ exceptions. Do they have to use the _nothrow_ syntax everywhere the
_new_ operator is used?

[0]
[https://www.cplusplus.com/reference/new/operator%20new[]/](https://www.cplusplus.com/reference/new/operator%20new\[\]/)

~~~
twic
My guess would be that they lean on the fact that "malloc never fails" [1].
Under that model, new and malloc always give you a pointer you can use, it's
just that sometimes, using it will give you a segfault.

[1] [https://scvalex.net/posts/6/](https://scvalex.net/posts/6/)

~~~
MaxBarraclough
That blog post describes Linux's default 'memory overcommitment' behaviour.
Oddly it fails to mention the term 'overcommit'.

As the post says, malloc _can_ return null even where overcommit is used; it's
not true that malloc never fails when overcommit is enabled.

Even if it were true that malloc/calloc/realloc/new/new[] could never fail on
your OS+compiler, it doesn't seem wise to write code that relies on that non-
standard guarantee. Poor robustness and portability.

------
wtetzner
I feel like I'm missing something. Even if we assume i is never 4 or 5, why
can we assume that v is contained in the table? Even with undefined behavior,
it seems like that's bugger compiler behavior.

By the author's logic, even if the undefined behavior didn't exist, you can
still assume i is never 4 or 5, but that doesn't meant the function always
returns true. Is there something I'm not understanding here?

~~~
Arnavion
Once the i == 4 and i > 4 cases have been eliminated, all code paths in the
function return true. It's helpful to remember that the

    
    
        if (table[i] == v) return true;
    

line has an implicit `else continue;` clause, not an `else return false;`
clause.

~~~
wtetzner
Ok, so because i never reaches 4, it assumes the loop doesn't exit due to the
'for' condtion, so it must return true.

That makes sense, but still feels like a broken way to do optimizations.

------
SloopJon
I don't remember what UBSan looked like in 2014, but it does something not
entirely unhelpful for each of these examples, although they are caught at run
time, not compile time:

runtime error: index 4 out of bounds for type 'int [4]'

UndefinedBehaviorSanitizer: SEGV on unknown addres

runtime error: reference binding to null pointer of type 'int'

~~~
logicchop
> although they are caught at run time, not compile time

Not if the compiler got to it first. If the compiler does what Chen points out
it can do, you won't be indexing anything at run time.

~~~
saagarjha
At -O0, the compiler will generally not do any of that analysis.

~~~
klodolph
GCC doesn’t enable -fdelete-null-pointer-checks until -O2, so even -O1 might
work fairly well.

------
dang
Discussed at the time:
[https://news.ycombinator.com/item?id=7954944](https://news.ycombinator.com/item?id=7954944)

~~~
JadeNB
Or maybe the discussion time travelled …?

