Hacker News new | ask | show | jobs
by lmm 1 day ago
> In general on a system where you trap when accessing the zero page, this optimization should be safe and a null pointer dereferences should (safely) trap.

If you mean that C compiler writers "should" prioritise sanity over high scores on microbenchmarks, then I agree. However in practice they do not and this optimization is not remotely safe.

1 comments

Do you have any evidence for this? On GCC it should be safe.

(EDIT: what is not safe is indexing into a null pointer. For this you need to be safe you need -fsanitize=null)

I don't understand your comment - dereferencing a null pointer is unsafe, in the sense that it does not reliably crash but may do other things, as we saw in the kernel case we're talking about. Yes that particular case was only exploitable if you mapped the zero page, but given how all-bets-are-off a situation it created (where extremely experienced programmers thought they knew what the code did, thought it was safe, and were wrong), I would not want to count on all cases not being exploitable without mapping the zero page.
May. If. If. If. In case.

We are talking about an extremely simple straightforward API with an obvious contract. It's good enough for this function to reliably surface almost all wrong uses with a segfault immediately. Wrong use will result in segfaults and otherwise bugs and crashes. The goal is not to work when used wrong but to work when used right. You cannot save the world from scratch in every little function. You still have a job to get done, and you have to move on.

> You cannot save the world from scratch in every little function. You still have a job to get done, and you have to move on.

Or you can take all of 10 minutes to put sanity-check assertions at the start of all your public-facing API functions, eliminating a source of security bugs, get on with your life, and worry about the performance implications as and when it becomes a problem (hint: it's never going to become a problem).

You can try and do this if it's a relatively narrow public facing API, but otherwise this is a theoretic ideal. In practice, if you add an assertion for every pointer argument to every little function, you'll go insane, and it is completely pointless, and the code will not be readable anymore.

There are so many other interesting and relevant invariants that are usually in an API contract that are much harder or impossible to check upfront (let alone express formally in a type system), and even violations may be impossible to diagnose when they happen.

People focus on NULL because that's the only way they can apply their silly limited type systems. But NULL checks give very little return for investment. In practice, you'll see templated Option<T> types and whatnot, and when I have to look at or even work with such code I want to kill myself because it's so painful.

No, people focus on a handful of things like null, buffer overrun, and use-after-free because they still make up the majority of security vulnerabilities that we see exploited in the wild. You may imagine that subtle logic errors are more common, but the data doesn't bear that out; also FWIW I've never seen one of these detailed invariants be impossible to express in a type system if you spend 5 minutes actually trying.
It takes a lot longer to figure out if it'll be a problem than to just add the check. And you don't have to ponder whether it's possible for a null to get there, because now it's fine if it does.
Are you talking about extending the API contract to allow for NULL? That is often the path to madness, especially if it requires complicating the signature (return value etc). Better to just assert/crash.