If you were only afraid of the undefined behavior, you could augment the compiler to insert runtime checks anywhere undefined behavior could occur (which obviously can be done with Clang sanitizers.) However, the undefined behavior problem is really just a symptom of incorrect code, so it'd be even better if we could just prevent that instead.
In high level languages like Java and Python there is just as much, if not more, interest in preventing null reference exceptions, even though they are "safe".
The kernel is one such exception.
That said, a potentially bigger problem is what happens when handling it. Instead of a kernel panic, nowadays you get a kernel oops. That's definitely going to have weird side-effects that could have e.g. security implications. But honestly, this all goes back to the original problem: in a lot of cases, there just isn't really a more sensible thing to do anyways. Even if the null dereference itself is potentially scary, by the time you get to the point where it might happen, you've already missed the actual underlying problem, and there might not be anything reasonable you can do.
I will grant you though that there are definitely some exotic cases where null dereferences won't trap. But this wasn't the point, I glossed over it for a reason.
Perhaps the most important exception is when the optimizer assumed the pointer was non-null, so optimized it in a way that produces completely unexpected behavior when it is null.
Also use-after-free and use of uninitialized pointers is more likely to point to incorrect, but mapped, locations.
> Also use-after-free and use of uninitialized pointers is more likely to point to incorrect, but mapped, locations.
I stuck to a null pointer dereference because it's useful for demonstration since the side-effect of hitting one is usually not a huge deal, but actually it wouldn't matter if it were a huge deal or not. The point I'm trying to make, and maybe not making obvious enough, is that the null pointer dereference is just a symptom of the fact that other invariants are not being upheld; it's not just about preventing an unsafe operation, it's about preventing the kinds of incorrect code that lead to them. It's the same for a use-after-free. That's exactly why I am a fan of Rusts' borrow checker, you can statically eliminate the problem that causes use-after-frees.
It isn't really that hard to construct a memory safe programming language, but the "obvious" ways of doing it have trade-offs that are undesirable or infeasible for some use cases. Rather than make the operations "more safe" by ducktaping runtime checks, Rust just forces the code to be more correct by statically checking invariants.