Huh?

The main threat model that reproducible builds are meant to guard against is simple: an attacker, who has write access to a binary distribution of some piece of software, uploads a malicious binary rather than the true output of compiling the corresponding source.

With a reproducible build, anyone can prove that a given binary is non-malicious by re-running the build and verifying that the output is the same as the binary they downloaded. This leaves the problem of ensuring that everyone gets the same binary, i.e. the server distributing binaries hasn't been modified to serve different files to different IP addresses or something like that. But there are ways to solve that: for example, you could have multiple independent parties who each verify all the builds and sign them with their keys, and end users could check for signatures from N different trusted parties before installing.

Maybe you understand this, but I have a hard time seeing how many of the things you propose in any way substitute for reproducibility.

> If you have a known, good state, then you don't have to. You simply use the source or binaries in the repo after checking hashes or signatures on a few mirrors. If your state is that bad, then you're already compromised with possible a rootkit.

Checking signatures on a few mirrors is nice, but you still have to trust the single machine where the package was originally built. Reproducible builds let you avoid that.

If you use no binaries at all, i.e. you start with some existing (trusted) OS with a compiler already installed, and from that OS you build the entire target OS from scratch, then sure, you don't need reproducible builds. But nobody installs operating systems that way. The vast majority of OSes don't even support being built from any OS other than themselves. As has been mentioned in this thread, NetBSD is a partial exception as it can build from any POSIX system, but that still rules out building from, say, Windows. Much easier to just verify some GPG signatures on your OS of choice, at least if you trust that the N verifiers won't collude or all be hacked, etc.

> That takes way more than reproducible builds. You need source code for a compiler that doesn't have 0-days in it in terms of backdoors, optimizations that eliminate security checks, or passes that create exploitable faults. That's called a verified compiler.

A verified compiler would be nice to have in many cases (with the drawback that the output binaries are usually rather slow), but that's pretty much orthogonal to reproducible builds, as the threat models of "0-day via bad compiler optimization" and "binary distribution compromised" are completely separate. Compiler backdoors are also orthogonal, since a binary purportedly corresponding to a verified compiler can still be backdoored (the verification is usually done on the compiler's source)...

> The other problem is that making all binaries come out equal creates more of a monoculture that aids attacks on endpoints. Many good tools in the field automatically harden C programs by making them memory or data-flow safe. Softbound+CETS comes to mind. Others obfuscate the hell out of them on a per-user basis to reduce odds of one-size fits all attack. You can't use these techniques with everyone having same binary.

If you want to customize binaries per-user, or use different compiler settings/passes from upstream, then that's great. In that case, reproducible binaries are still useful for the initial bootstrap step, as I mentioned above.

But I don't know what that has to do with hardening passes. There is nothing inherently nondeterministic about those. They can be part of the upstream build, in which case they should be reproducible, or they can be not part of it, in which case you need to build from source if you want them. But in that respect they don't differ from any other compiler option or compiler variant.