This probably could have been fixed with basic testing.
This probably could have been fixed with basic testing.
Just look at all the companies like Microsoft or Apple with millions to spare, or massive community efforts like Linux kernel, either with no shortage of means and resources to make their systems resistant to simple programming bugs.
But history clearly shows that no amount of processes, audits, static analysis or eyepairs help: bugs just keep on being found where ever security folks look for them, and the harder they look, the more they find. Upside is, these systems can be patched, which is comforting as clearly there just is no bug-free code.
Yet, people keep on pouring millions of dollars worth of virtual tokens to these experimental blockchain systems that are fundamentally designed so that any bug of certain class means the money is forever, irreversibly lost - as if these crypto contracts were some mythical new breed of software written by infallible Gods.
But its proving in my experience to be a much more practical foundation, and going forward there is a lot of value in that.
Security-related compiler bugs very rare, but they do exist, at least in graphics shader compilers: http://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/OOPS... . My impression is that many of these shader compilers are based on LLVM, but this is mostly conjecture. (I do recall a video game crashing on me with internal LLVM errors from the graphics driver.)
Bugs happen no matter the language, even if LLVM was in Rust it would have them.
For instance:
sudo rm -rf /
Every part of that is completely sound and correct. There are no buffer overflows, emory corruption, or anything. You wrote a completely correct command to do something (probably) really wrong.Then there are systems that just prevent ambiguous rules if you really dig into it. (Speaking of assisted theorem prover such as Coq and Isabelle.)
In this case it was fully specified that sudo should run rm with all privileges, which in turn will recursively deleted the root filesystem.
The entire execution is fully specified and will execute without fault. There is no mistake in `sudo rm -rf /`.
Logical faults may not be preventable with provers, such as when underlying assumptions are wrong.
For example, a prover could have verified the parity library as fully okay because it assumes that the initialization function will not be called without delegation. This underlying assumption X -!> Y (X cannot lead to Y) is wrong and X -> Y is the case but the prover only verified that Y -!> X.
Provers essentially duplicate your code, forcing you to express the solution and/or problem twice such that if you basically typo on the way, one of the pieces will complain about the other.
But it cannot and is incapable of preventing higher level mistakes.
The fact that you can do these things doesn't save you from people who don't. Parity's last audit happened before they made a major architectural change; if they'd gotten new audits this probably would have been caught. They also have the most complex wallet code I've seen, often dipping into assembly.
It's true that you can't be completely sure of avoiding bugs, but with decent practices I think it's less of a problem on Ethereum that it is in medical equipment, airliners, and nuclear reactors.
You probably won't do it on the first try. And you wouldn't immediately bet a million dollar on it. But is it possible?
Yes.
A multisig wallet shouldn't be more complicated.
Could you prove the correctness of the 6502 chess? Sure. Formal methods are not black magic. You need a semantics of the machine and a formalization of the rules.
Can you make mistakes in specifying? Yeah, duh. So combine it with peer review and throw in a bug bounty and fuzzing.
https://en.wikipedia.org/wiki/Castling#Requirements
and some people may neglect or misimplement en passant
https://en.wikipedia.org/wiki/En_passant
and the rules related to repetitive situations in the end game:
https://en.wikipedia.org/wiki/Threefold_repetition
https://en.wikipedia.org/wiki/Fifty-move_rule
(Implementing the threefold repetition rule requires maintaining historical state of a different kind than any other chess rule!)
I've seen impressive hyperminimalist chess implementations that were missing these things, but that totally felt like real chess almost all of the time.
A perfect chess implementation from before those rule changes would no longer be correct!
It’s possible to prove that code perfectly implements a formal spec. It’s tough to prove that the formal spec perfectly describes what you want it to describe.
This is a very strong if. It means the rule cannot be reduced in plain logic.
The example of forward castling rule is a good one.
A missing definition of some en passant situations is caught too. (E.g. situations of check.)
Completely missing rule cannot be caught with these techniques. You also need to ask the prover right questions.
Say: when does a chess game end. Proof requires proving the existence of a halting Oracle for any game state. Not quite easy but possible. To actually verify, you will have to provide reduction rules unless you happen to own a supercluster.
It takes time and sometimes serious math skills.