Knowing the story behind a decision like that is worth a thousand spec clauses.
When the language got operator overloading there was the question of how to convert the arguments from the operator itself to its implementation. You wanted to write a+b, not &a+&b but most versions of operator+ were going to want const pointers for efficiency. One option was to make the & implicit, another was to introduce a type of argument that didn't need & but acted like a pointer. So references were born.
I ignore people who advise to use references when something can't be null. A null reference is UB just like a null pointer, it just shifts where the UB is, but the compiler is likely to produce the same code either way and the program is just as likely to crash. Instead, I use pointers to make it obvious that something is a pointer, and references when it should be invisible, just like with operator overloading.
One of the first production C++ codebases I worked on had helpers:
Type& GetReference(Type*);
(Type is a placeholder, it was not templated). It worked exactly how you'd fear it would, and as a result we had to chase down null dereferences of our "can't be null" references all over the codebase.EDIT: by "exactly how you fear it would", it just did a dereference and return. It didn't check for null, didn't throw, and couldn't otherwise signal a failure.
The source of the UB is dereferncing a null pointer. I don't think it's possible to obtain a null reference otherwise. I, on the other hand, ignore people who advise against using references for parameters that can't be nullable. Particularly const references, which provide potentially massive performance improvements, and are a core idiom in C++.