Ironsides DNS Server in Ada Spark
ironsides.martincarlisle.com
ironsides.martincarlisle.com
Sounds like famous last words. How can something be provably invulnerable to RCE? Wouldn't you just be able to prove that it's invulnerable to known RCE's?
A program could also be implemented in a safe language where the defined semantics rule out RCE. Of course this would then depend on how much trust and/or verification is put into the language implementation and specification corectness.
It doesn't seem like such a language would be very useful, unless you mean a language developed specifically for a particular application.
A language can be made safe against invalid memory accesses and undefined behavior but if it prevents RCE then it must also prevent implementing remote procedure calls, meaning it's not general purpose.
There are many pieces of software that allow a remote user to execute arbitrary code remotely. Some examples are ssh and the Jupiter notebook running a Python or Julia kernel.
Of course these types of software usually include an authentication/authorization mechanism limiting access to authorized users. Any bug in the implementation of this mechanism could potentially allow the unauthorized remote execution of arbitrary code. No "safe" language alone can prove the absence of all such bugs. At a minimum doing so would require a complete specification of who is authorized to execute what code and how they are to prove their identity to the software along with a formal proof that the implementation completely implements the spec.
First let me be clear that this comment only relates to the parent's statement "A program could also be implemented in a safe language where the defined semantics rule out RCE." It is this claim I take issue with, not anything about the original post, which I haven't looked at in detail. Spark allows contracts and pre- and post- conditions to be defined on procedures which together may amount to encoding a spec for the application into the source code. It may then be possible to prove the implementation correct relative to this spec. But that's very different from saying that the defined semantics of the language alone (ie. without the spec which must be provided to the verifier one way or another) prevents RCE.
Now to substantiate my claim that the semantics of a general purpose language cannot alone "rule out RCE".
1) A general purpose language may be used to implement a program which provides an RPC (remote procedure call) mechanism, whether open to all or restricted to a set of authorized users.
2) A program written in a general purpose language may implement an interpreter for its own, or some other language, such as Scheme.
3) In the absence of a formal spec nothing prevents the programmer from implementing an RPC call which takes a string received from the network, with no authorization/authentication checks, and runs that string through the program's scheme interpreter. This constitutes RCE by any definition I'm aware of.
There was a proof of SSL or TLS (can’t remember which) as a protocol was secure, but later was exploited. This was because the exploit was something the spec didn’t cover
Also, a bug in the compiler could introduce such a bug.
To my knowledge, the major Ada compilers have a very good reputation for correctness, but none are formally verified, and there's no such compiler on the horizon.
> There was a proof of SSL or TLS (can’t remember which) as a protocol was secure, but later was exploited. This was because the exploit was something the spec didn’t cover
Typically we'd expect the only vulnerabilities in such an implementation would be side-channel attacks, such as timing attacks. Do you have any more details on this? I couldn't find anything with a quick google.
My understanding is that both Needham–Schroeder and SSL had formal verification prior to the breaks, but the breaks relied on properties outside of what was proven (which was thought to be complete at the time).
The relevant content is mostly on page 2. They seem to be saying that when it comes to crypto, both the requirements work and the implementation work are both challenging and error-prone, even for expert practitioners. I don't think they're exactly saying anything against formal methods, though:
> It is significant, for example, that the above mentioned flaw affecting the Needham-Schroeder public-key protocol could be found by applying formal methods
Later, under Motivation, regarding what are essentially integration bugs:
> in OpenSSL an error condition returned by a cryptographic function was incorrectly interpreted by the function caller, making the application accept corrupted data; such a fault cannot be found if a formal model that has no relation with the implementation code is analyzed
Not exactly a demonstration of a weakness of formal methods. Instead, an instance of a bug (a normal one, not a side-channel vulnerability) occurring in a system which was only partially formally verified.
> a large gap still exists between these models and a real-world protocol implementation and its execution.
A problem with real-world implementation, not with formal methods. I wonder what they would have made of 'SPARKSkein', released around the time that paper was published. [0] I suppose it wouldn't have been anything all that new - integration bugs could still occur.
The other question is vulnerability to side-channel attacks. Today's formal methods aren't much use against them.
> Also, a bug in the compiler could introduce such a bug.
> To my knowledge, the major Ada compilers have a very good reputation for correctness, but none are formally verified, and there's no such compiler on the horizon.
No, a bug in the compiler does not introduce such a bug.
A bug in the compiler is a bug in the implementation and does not equate to a bug in the formal spec. It is rather like a fault in a machine or a building arising from a dodgy supplier using below spec materials, or a fault in the manufacturing or assembly process.
> A bug in the compiler is a bug in the implementation and does not equate to a bug in the formal spec.
We weren't discussing the challenges of creating a correct formal specification. I presume you meant a bug in the formally verified implementation, rather than a bug in the formal spec, in which case:
More precisely then: a compiler bug could introduce a defect into the final system, which was not present in the formally verified implementation source code. (This assumes that the scope of the formal verification stops at Ada source code.)
> It is rather like a fault in a machine or a building arising from a dodgy supplier using below spec materials, or a fault in the manufacturing or assembly process.
Those are all risks that mechanical engineers need to deal with carefully. If it's your job to deliver a defect-free avionics software binary, then dealing with the risk of compiler bugs is part of that. A company like Airbus isn't going to treat compiler bugs as Eh, not our problem. Presumably they're going to be careful in their choice of compiler, and back it up with testing.
My point was that a bug in the compiler is similar to a bug in hardware, in that it opens the door to defects which are formally assured to be absent from the verified implementation's source code.
It's possible to use formal verification to guarantee against issues arising from compiler bugs, it's just that I don't think this can currently be done with SPARK. There are two formal approaches to protect against compiler bugs: either use a formally-verified compiler (like the CompCert C compiler), or use a non-verified compiler and then verify the correspondence between the input source and the generated machine-code (the approach used by Sel4).
Try writing some moderately complex progra. in C, then a formally verified version in SPARK and fuzz them. That is usually quite confidence-inspiring :)