Peculado bancario a partir de la crisis financiera del año
1. LEY 99-26 PUBLICADA EN REGISTRO OFICIAL NO 190, DE 13 DE MAYO DE
Only a negligible portion of real software is suitable for direct formal verification. Also specification of requirements is rarely given as a set of formulae. The mathematical proof of correctness of the whole program is not the only goal that is worth reaching.
We view formal methods as an advanced testing tool. It is really hard to prove that a program is ultimately correct; it is, however, not so hard to prove that some particular thread interaction scheme is deadlock free. Moreover, the effort to formally prove even some trivial properties leads to a better structure of the code as the developers have to separate individual aspects of the application more precisely in order to make the verification feasible. The positive influence of the formal methods should work in the same way as in the case of test-driven
development [58].
Examining Possibilities
There are plenty of ways how to support formal methods and every way has its own pros and cons; there is not any universal verification process. Selection of the verification process depends on the set of properties that needs to be verified. Thus we can do nothing better than to evaluate some possibilities and choose the most promising one. We are especially interested in explicit model checking because it can be applied to real programs.
First, it does not seem promising to verify the final output code, i.e., C or machine code. This code is full of low-level implementation details. The more abstract the code is, the better.
The process of translation from RPython to C is about adding implementa- tion details. The input high level code is rather abstract, it relies on a garbage collector, assumes objects and exception handling as native features. Contrary, the generated C code explicitly implements these features on the lower level of
abstraction. Thus verification of the RPython source code is easier than verifi- cation of the C code.
The second possibility is to generate some special version of the final C code. Instead of sequential run, the generated program would enumerate all possible states. This is patterned after the SPIN model checker. SPIN translates a formal model specified in Promela language to C code. The C code is a one-purpose model checker that solves the model checking problem for the specified formal model. So this approach is feasible; however, it would require a tremendous amount of work to implement it.
The third possibility is to add some verification facility to the PyPy compiler itself. During the compilation, PyPy uses a technique called abstract interpreta-
tion in order to create a data structure called flow-graph from the Python byte-
code. The abstract interpreter can be possibly tweaked into a model-checker that enumerates the program’s state space. The result would be an explicit model checker patterned after Java Pathfinder. We believe that this approach is also possible; however, also requires amount of of work we can not afford.
The fourth possibility relies on a simpler PyPy modification. Instead of generating C code, we would generate an input code for some verification tool, such as the SPIN model checker. The main disadvantage is that Promela is not a general purpose programming language. It lacks some features such as floating point computations and synchronization primitives based on channels differ significantly from common locks and semaphores.
The Selected Tool: Java Pathfinder
We finally decided for the fifth way. We let the PyPy compiler to generate Java byte-code, then we verify the code by explicit model checker called Java Pathfinder.
Java Pathfinder is one of the most known verification tools, see also section 3.1.3. Its main advantage is that it does not verify any special purpose modeling language but the real program code, literally, Java byte-code.
Java byte-code can be generated by the PyPy compiler more straightfor- wardly than C code because RPython and Java are similar in many ways. Both languages are object-oriented, garbage collected, have exception handling.
We use the Java byte-code as a modeling language. It is slightly more ab- stract than the C code we intend to deploy. On the other hand, the abstraction gap between C and Java-byte-code is not so wide as in the case of, say, Promela, because Java byte-code is a binary representation of general-purpose program- ming language.
To make the verification process meaningful, we have to guarantee that the C code and the Java byte-code are equivalent. It is obvious that the two codes
Chapter 5. The Proposed Development Approach
that are run on different platforms can not behave exactly equally. However, there are also many properties that are the same in both codes. For instance, the deadlock found by Java Pathfinder in the Java byte-code means that there is certainly a deadlock in the C code—under the condition that the semantics of synchronization objects is equal.
Our verification process also can take advantage of dynamic approach. The Java Pathfinder verifies a real program code. However, in practice, one often have to build a simplified version of the program to make the verification feasible, e.g., extract the critical part. The dynamic nature of Python—the dynamic creation of program’s object space—may help here. As object interconnections in dynamic languages are more loose, it is easier to generate a version of the program that contains some mockup objects instead of full-featured objects.