
My first experience with formal methods - matt_d
http://zipcpu.com/blog/2017/10/19/formal-intro.html
======
indubitable
I think this is a very typical first response to formal methods. But
eventually it becomes clear that ultimately it's not really solving any
problem, but simply kicking the can to another problem. And in my opinion it's
a step backwards as the can you're kicking it to is arguably more complex than
where you came from.

Depending on the system you're using formal methods will rely on some set of
preconditions (state before a function), postconditions (state after a
function), and general state. There's nothing magical about formal methods
that prevents errors, either logical/technical or by omission, here. And
suddenly very small changes in program flow/logic can result in enormous
amounts of work.

They're really fun and interesting, and also help you look at your code in a
different way even when not using them. I think everybody has this sort of
'epiphany' moment the first time they actually play with them. But they don't
work out in practice anywhere near as well as they do in theory or very
limited scale. Something you quickly realize as the effort involved increases
exponentially against linear increases in program size/complexity.

~~~
touisteur
Without going to full functional proof in Spark (for example) you can go
easily as a first step for proof of data flow peoperties ('ooops this buffer
size variable is not initialized in this strange corner case'), and then
without too much effort (compared to exhaustively testing) proof of absence of
runtime error (aka this code won't crash). Of course as GP remarks, you'll
have explicited all your preconditions and invariants (types or contracts).
But that's a choice : either treat the damned corner cases in your code or
expose them to your user ("this can't work on input buffers that aren't power-
of-2"). If you don't treat the corner case, instead of putting it in the docs,
or in the header comments, or asserting right when you start, add it as a
precondition or type invariant/predicate. You then get all the power of
automated proof tools (please use all my cycles and RAM but prove this) to
prove that without the corner case you won't crash and your users get to check
(by proof, static analysis, testing, fuzzing, 'careful code reading'...) that
they never call your API the wrong way - if they want.

I think Frama-C & Spark should sell a bit more the AoRTE stuff. If at least
your software won't crash or use uninitialized variables, you'll sleep better.
The world would probably be a better place if things like x509, asn1 decoding
code and all kinds of file format parsers were proved for 'just' the absence
of runtime error...

The thing that is nice with most modern formal verification tools is that
they're not all-or-nothing propositions. Suppose you already have some Spark
code that you've checked (proof) for AoRTE, but didn't go for full proof of
all the functional properties of your software (you went for pure-testing with
a pinch of static analysis and some code review on critique stuff). You're
already high on the correctness scale... You get a report from a customer or
security researcher or code auditor that there is a strange behaviour in your
code. They can't nail the reason but sometimes you send inconsistent data. No
crash or memory corruption, just an output that seems impossible to you (even
after full code review and exhaustive test) and out of spec anyways. No
reproducer, no idea what was sent to your system, just 'please stop sending us
inconsistent data'. One way to find the bug is to try to prove it's not there.
Add a post-condition 'fields a and b and c should never be 0 at the same time'
and use the tools. Maybe start with structured testing following the code
yourself... Then go for unsound static analysis or fuzz testing. Then once
you've ran 2 weeks of afl and hongfuzz and didn't trigger this post- and once
you've exhausted all the codepeer (or PVS studio or Coverity) false positives
and still they didn't find the code path to your bug, and you still need to
find this bug, you can use the proof tools and be sure you'll get an answer
(after some work) on this specific question.

2 big caveats (for Spark at least) : you'll have to restrict yourself to the
Spark subset of Ada (mostly, no pointers, no aliasing - but this subset is
deemed to grow once the proof tools improve...) and second : automated proof
with floating point numbers is not there yet and it is a _hard_ research
topic.

~~~
pron
It's worth mentioning that a mature code-level specification language similar
to Frama-C's ACSL and SPARK also exists for Java -- JML (see
[http://www.openjml.org/](http://www.openjml.org/)). In fact, ACSL is based on
JML.

~~~
touisteur
True, and there was a question on this topic during the Frama-C and Spark day
this year in may in Paris, about similar tools and environments in java.
People in the room cited JML and it being the inspiration for ACSL but the
conclusion seemed to be that the proof technology wasn't so advanced and
supported ? I'm very interested if you have more info on this, I'd be happy to
be mistaken.

If you look around there's also EVE (for Eiffel) that B. Meyer mentionned some
time ago on his blog. They didn't have to add a specification language or to
supplement their original language, it was built in Eiffel from the start :-D.

One funny thing about specification languages is that they 'need' to be
expressive to be useful. The Ada 2012 specs added lots of sugar to help
writing contracts : quantifiers on collections/arrays (for some, for one), if-
and case- expressions (pattern matching isn't far) and expression functions...
and in Spark 2014 contract-cases. I expect some more in Ada2020. Since
contracts _must_ be executable you get all the sugar for your regular Ada too.

~~~
pron
> but the conclusion seemed to be that the proof technology wasn't so advanced
> and supported

That depends on the tool. OpenJML[1] just uses SMT solvers, but if you need
bigger guns, Krakatoa[2] lets you use either SMT solvers or a proof assistant
(either Isabelle or Coq).

[1]: [http://www.openjml.org/](http://www.openjml.org/)

[2]: [http://krakatoa.lri.fr/](http://krakatoa.lri.fr/)

------
auggierose
Formal methods are particularly effective for verifying hardware, because a)
the state space is much smaller than for software (although it can still be
prohibitively large for complex designs and brute force formal methods) b)
people are actually ready to shell out some decent money for getting the
verification done.

~~~
throwaway40483
Not only is formal methods more effective for HW, the incentives are also more
tilted towards HW. If you fuck up the HW design, well..you're fucked. With SW,
"you can always push out an update" mentality exists. I'm not saying this to
criticize SW, but this is more an example of how you conform to the ecosystem
in which you exist.

~~~
DennisP
Which is why people are keen on developing formal methods for smart contracts,
which generally can't be updated. Plus they're a lot smaller than most
software.

