DOKK Library

Runtime Error Annotation Generation For Frama-C 23.0

Authors Julien Signoles Philippe Herrmann

License CC-BY-SA-4.0

Plaintext
RTE — Runtime Error Annotation Generation
               Frama-C’s RTE plug-in
                      for Frama-C 23.0 (Vanadium)




                        Philippe Herrmann and Julien Signoles


This work is licensed under a Creative Commons “Attribution-
ShareAlike 4.0 International” license.


                          CEA-List, Université Paris-Saclay
                          Software Safety and Security Lab




©2010-2021 CEA LIST
                                            CONTENTS




                                                                               Contents

1 Introduction                                                                                   7
  1.1   RTE plug-in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     7
  1.2   Runtime errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .      7
  1.3   Other annotations generated . . . . . . . . . . . . . . . . . . . . . . . . . . .         8

2 Runtime error annotation generation                                                            11
  2.1   Integer operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     11
        2.1.1   Addition, subtraction, multiplication . . . . . . . . . . . . . . . . . . .      11
        2.1.2   Signed downcasting . . . . . . . . . . . . . . . . . . . . . . . . . . . .       12
        2.1.3   Unary minus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .      13
        2.1.4   Division and modulo . . . . . . . . . . . . . . . . . . . . . . . . . . . .      13
        2.1.5   Bitwise shift operators . . . . . . . . . . . . . . . . . . . . . . . . . . .    14
  2.2   Left-values access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   15
  2.3   Unsigned overflow annotations . . . . . . . . . . . . . . . . . . . . . . . . . .        17
  2.4   Unsigned downcast annotations . . . . . . . . . . . . . . . . . . . . . . . . . .        17
  2.5   Cast from floating-point to integer types . . . . . . . . . . . . . . . . . . . . .      18
  2.6   Expressions not considered by RTE . . . . . . . . . . . . . . . . . . . . . . . .        18
  2.7   Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   19
  2.8   Undefined behaviors not covered by RTE . . . . . . . . . . . . . . . . . . . . .         20

3 Plug-in Options                                                                                21

Bibliography                                                                                     23




                                                5
                                                                            Chapter 1

                                                                  Introduction


1.1    RTE plug-in
This document is a reference manual for the annotation generator plug-in called RTE. The
aim of the RTE plug-in is to automatically generate annotations for:

  • common runtime errors, such as division by zero, signed integer overflow or invalid
    memory access;
  • unsigned integer overflows, which are allowed by the C language but may pose problem
    to solvers;

In a modular proof setting, the main purpose of the RTE plug-in is to seed more advanced
plug-ins (such as the weakest-preconditions generation plug-in [2]) with proof obligations.
Annotations can also be generated for their own sake in order to guard against runtime
errors. The reader should be aware that discharging such annotations is much more difficult
than simply generating them, and that there is no guarantee that a plug-in such as Frama-C’s
value analysis [3] will be able to do so automatically in all cases.
RTE performs syntactic constant folding in order not to generate trivially valid annotations.
Constant folding is also used to directly flag some annotations with an invalid status. RTE
does not perform any kind of advanced value analysis, and does not stop annotation gener-
ation when flagging an annotation as invalid, although it may generate fewer annotations in
this case for a given statement.
Like most Frama-C plug-ins, RTE makes use of the hypothesis that signed integers have
a two’s complement representation, which is a common implementation choice. Also note
that annotations are dependent of the machine dependency used on Frama-C command-line,
especially the size of integer types.
The C language ISO standard [4] will be referred to as ISO C99 (of which specific paragraphs
are cited, such as 6.2.5.9).


1.2    Runtime errors
A runtime error is a usually fatal problem encountered when a program is executed. Typical
fatal problems are segmentation faults (the program tries to access memory that it is not




                                             7
                                  CHAPTER 1. INTRODUCTION



allowed to access) and floating point exceptions (for instance when dividing an integer by
zero: despite its name, this exception does not only occur when dealing with floating point
arithmetic). A C program may contain “dangerous” constructs which under certain conditions
lead to runtime errors when executed. For instance evaluation of the expression u / v will
always produce a floating point exception when v = 0 holds. Writing to an out-of-bound
index of an array may result in a segmentation fault, and it is dangerous even if it fails to
do so (other variables may be overwritten). The goal of this Frama-C plug-in is to detect
a number of such constructs, and to insert a corresponding logical annotation (a first-order
property over the variables of the construct) ensuring that, whenever this annotation is
satisfied before execution of the statement containing the construct, the potential runtime
error associated with the expression will not happen. Annotation checking can be performed
(at least partially) by Frama-C value analysis plug-in [3], while more complicated properties
may involve other plug-ins and more user interaction.
At this point it is necessary to define what one means by a “dangerous” construct. ISO C99
lists a number of undefined behaviors (the program construct can, at least in certain cases,
be erroneous), a number of unspecified behaviors (the program construct can be interpreted
in at least two ways), and a list of implementation-defined behaviors (different compilers
and architectures implement different behaviors). Constructs leading to such behaviors are
considered dangerous, even if they do not systematically lead to runtime errors. In fact an
undefined behavior must be considered as potentially leading to a runtime error, while un-
specified and implementation-defined behaviors will most likely result in portability problems.
error prevention.
An example of an undefined behavior (for the C language) is signed integer overflow, which
occurs when the (exact) result of a signed integer arithmetic expression can not be represented
in the domain of the type of the expressions. For instance, supposing that an int is 32-bits
wide, and thus has domain [-2147483648,2147483647], and that x is an int , the expression
x+1 performs a signed integer overflow, and therefore has an undefined behavior, if and only
if x equals 2147483647. This is independent of the fact that for most (if not all) C compilers
and 32-bits architectures, one will get x+1 = -2147483648 and no runtime error will happen.
But by strictly conforming to the C standard, one cannot assert that the C compiler will
not in fact generate code provoking a runtime error in this case, since it is allowed to do
so. Also note that from a security analysis point of view, an undefined behavior leading to a
runtime error classifies as a denial of service (since the program terminates), while a signed
integer overflow may very well lead to buffer overflows and execution of arbitrary code by
an attacker. Thus not getting a runtime error on an undefined behavior is not necessarily a
desirable behavior.
On the other hand, note that a number of behaviors classified as implementation-defined by
the ISO standard are quite painful to deal with in full generality. In particular, ISO C99
allows either sign and magnitude, two’s complement or one’s complement for representing
signed integer values. Since most if not all “modern” architectures are based on a two’s
complement representation (and that compilers tend to use the hardware at their disposal),
it would be a waste of time not to build verification tools by making such wide-ranging and
easily checkable assumptions. Therefore RTE uses the hypothesis that signed integers
have a two’s complement representation.


1.3    Other annotations generated

RTE may also generate annotations that are not related to runtime errors:




                                              8
                         1.3. OTHER ANNOTATIONS GENERATED



• absence of unsigned overflows checking. Although unsigned overflows are well-defined,
  some plug-ins may wish to avoid them.

• accesses to arrays that are embedded in a struct occur withing valid bounds. This is
  stricter than verifying that the accesses occurs within the struct.




                                         9
                                                                            Chapter 2

               Runtime error annotation generation

2.1     Integer operations

According to 6.2.5.9, operations on unsigned integers “can never overflow” (as long as the
result is defined, which excludes division by zero): they are reduced modulo a value which
is one greater than the largest value of their unsigned integer type (typically 2n for n-bit
integers). So in fact, arithmetic operations on unsigned integers should really be understood
as modular arithmetic operations (the modulus being the largest value plus one).
On the other hand, an operation on signed integers might overflow and this would pro-
duce an undefined behavior. Hence, a signed integer operation is only defined if its result
(as a mathematical integer) falls into the interval of values corresponding to its type (e.g.
[INT_MIN,INT_MAX] for int type, where the bounds INT_MIN and INT_MAX are defined in the
standard header limits.h). Therefore, signed arithmetic is true integer arithmetic as long as
intermediate results are within certain bounds, and becomes undefined as soon as a compu-
tation falls outside the scope of representable values of its type.
The full list of arithmetic and logic operations which might overflow is presented hereafter.
Most of these overflows produce undefined behaviors, but some of them are implementation
defined and indicated as such.


2.1.1   Addition, subtraction, multiplication

These arithmetic operations may not overflow when performed on signed operands, in the
sense that the result must fall in an interval which is given by the type of the corresponding
expression and the macro-values defined in the standard header limits.h. A definition of
this file can be found in the share directory of Frama-C.

                       type                     representable interval
                       signed   char            [SCHAR_MIN, SCHAR_MAX]
                       signed   short           [SHRT_MIN,SHRT_MAX]
                       signed   int             [INT_MIN,INT_MAX]
                       signed   long int        [LONG_MIN,LONG_MAX]
                       signed   long long int   [LLONG_MIN,LLONG_MAX]


Since RTE makes the assumption that signed integers are represented in 2’s complement, the
interval of representable values also corresponds to [−2n−1 , 2n−1 − 1] where n is the number




                                                11
                       CHAPTER 2. RUNTIME ERROR ANNOTATION GENERATION



of bits used for the type (sign bit included, but not the padding bits if there are any). The
size in bits of a type is obtained through Cil.bitsSizeOf: typ -> int, which bases itself on
the machine dependency option of Frama-C. For instance by using -machdep x86_32, we have
the following:

     type                     size in bits   representable interval
     signed   char                  8        [-128,127]
     signed   short                16        [-32768,32767]
     signed   int                  32        [-2147483648,2147483647]
     signed   long int             32        [-2147483648,2147483647]
     signed   long long int        64        [-9223372036854775808,9223372036854775807]


Frama-C annotations added by plug-ins such as RTE may not contain macros since pre-
processing is supposed to take place beforehand (user annotations at the source level can
be taken into account by using the -pp-annot option). As a consequence, annotations are
displayed with big constants such as those appearing in this table.

Example 2.1 Here is a RTE-like output in a program involving signed long int with an
x86_32 machine dependency:
     int main(void) {
       signed long int lx, ly, lz;

         /*@ assert rte: signed_overflow: -2147483648 <= lx*ly; */
         /*@ assert rte: signed_overflow: lx*ly <= 2147483647; */
         lz = lx * ly;

         return 0;
     }

The same program, but now annotated with an x86_64 machine dependency (option -machdep
x86_64):
     int main(void) {
       signed long int lx, ly, lz;

         /*@ assert rte: signed_overflow: -9223372036854775808 <= lx*ly; */
         /*@ assert rte: signed_overflow: lx*ly <= 9223372036854775807; */
         lz = lx * ly;

         return 1;
     }

The difference comes from the fact that signed long int is 32-bit wide for x86_32, and 64-bit
wide for x86_64.


2.1.2     Signed downcasting

Note that arithmetic operations usually involve arithmetic conversions. For instance, integer
expressions with rank lower than int are promoted, thus the following program:
     int main(void) {
       signed char cx, cy, cz;




                                                 12
                                    2.1. INTEGER OPERATIONS




         cz = cx + cy;
         return 0;
     }

is in fact equivalent to:
      int main(void) {
        signed char cx, cy, cz;

         cz = (signed char)((int)cx + (int)cy);
         return 0;
     }

Since a signed overflow can occur on expression ( int )cx + (int)cy, the following annotations
are generated by the RTE plug-in:
     /*@ assert rte: signed_overflow: -2147483648 <= (int)cx+(int)cy; */
     /*@ assert rte: signed_overflow: (int)cx+(int)cy <= 2147483647; */

This is much less constraining than what one would want to infer, namely:
     /*@ assert (int)cx+(int)cy <= 127; */
     /*@ assert -128 <= (int)cx+(int)cy; */

Actually, by setting the option -warn-signed-downcast (which is unset by default), the RTE
plug-in infers these second (stronger) assertions when treating the cast of the expression to a
signed char . Since the value represented by the expression cannot in general be represented
as a signed char , and following ISO C99 paragraph 6.3.1.3.3 (on downcasting to a signed
type), an implementation-defined behavior happens whenever the result falls outside the range
[-128,127]. Thus, with a single annotation, the RTE plug-in prevents both an undefined
behavior (signed overflow) and an implementation defined behavior (signed downcasting).
Note that the annotation for signed downcasting always entails the annotation for signed
overflow.


2.1.3     Unary minus

The only case when a (signed) unary minus integer expression -expr overflows is when expr
is equal to the minimum value of the integer type. Thus the generated assertion is as follows:
     int ix;
     // some code
     /*@ assert rte: signed_overflow: -2147483647 <= ix; */
     ix = - ix;


2.1.4     Division and modulo

As of ISO C99 paragraph 6.5.5, an undefined behavior occurs whenever the value of the
second operand of operators / and % is zero. The corresponding runtime error is usually
referred to as “division by zero”. This may happen for both signed and unsigned operations.
     unsigned int ux;
     // some code
     /*@ assert rte: division_by_zero: ux != 0; */
     ux = 1 / ux;




                                              13
                     CHAPTER 2. RUNTIME ERROR ANNOTATION GENERATION



In 2’s complement representation and for signed division, dividing the minimum value of an
integer type by −1 overflows , since it would give the maximum value plus one. There is no
such rule for signed modulo, since the result would be zero, which does not overflow.
     int x,y,z;
     // some code
     /*@ assert rte: division_by_zero: x != 0; */
     /*@ assert rte: signed_overflow: y/x <= 2147483647; */
     z = y / x;



2.1.5   Bitwise shift operators

ISO C99 paragraph 6.5.7 defines undefined and implementation defined behaviors for bitwise
shift operators. The type of the result is the type of the promoted left operand.
The undefined behaviors are the following:

  • the value of the right operand is negative or is greater than or equal to the width of
    the promoted left operand:
           int x,y,z;

           /*@ assert rte: shift: 0 <= y < 32; */
           z = x << y; // same annotation for z = x >> y;

  • in E1 << E2, E1 has signed type and negative value:
           int x,y,z;

           /*@ assert rte: shift: 0 <= x; */
           z = x << y;

  • in E1 << E2, E1 has signed type and nonnegative value, but the value of the result
    E1 × 2E2 is not representable in the result type:
           int x,y,z;

           /*@ assert rte: signed_overflow: x<<y <= 2147483647; */
           z = x << y;


There is also an implementation defined behavior if in E1 >> E2, E1 has signed type and
negative value. This case corresponds to the arithmetic right-shift, usually defined as signed
division by a power of two, with two possible implementations: either by rounding the result
towards minus infinity (which is standard) or by rounding towards zero. RTE generates an
annotation for this implementation defined behavior.
     int x,y,z;

     /*@ assert rte: shift: 0 <= x; */
     z = x << y;


Example 2.2 The following example summarizes RTE generated annotations for bitwise
shift operations, with -machdep x86_64:




                                             14
                                     2.2. LEFT-VALUES ACCESS



      long x,y,z;

      /*@   assert rte: shift: 0 <= y < 64; */
      /*@   assert rte: shift: 0 <= x; */
      /*@   assert rte: signed_overflow: x<<y <= 9223372036854775807; */
      z =   x << y;

      /*@ assert rte: shift: 0 <= y < 64; */
      /*@ assert rte: shift: 0 <= x; */
      z = x >> y;



2.2     Left-values access

Dereferencing a pointer is an undefined behavior if:

   • the pointer has an invalid value: null pointer, misaligned address for the type of object
     pointed to, address of an object after the end of its lifetime (see ISO C99 paragraph
     6.5.3.2.4);

   • the pointer points one past the last element of an array object: such a pointer has a
     valid value, but should not be dereferenced (ISO C99 paragraph 6.5.6.8).

The RTE plug-in generates annotations to prevent this type of undefined behavior in a sys-
tematic way. It does so by deferring the check to the ACSL built-in predicate valid(p):
valid(s) (where s is a set of terms) holds if and only if dereferencing any p ∈ s is safe
(i.e. points to a safely allocated memory location). A distinction is made for read accesses,
that generate \valid_read (p) assertions (the locations must be at least readable), and write
accesses, for which \valid (p) annotations are emitted (the locations must be readable and
writable).
Since an array subscripting E1[E2] is identical to (*((E1) + (E2))) (ISO C99 paragraph
6.5.2.1.2), the “invalid access” undefined behaviors naturally extend to array indexing, and
RTE will generate similar annotations. However, when the array is known, RTE attempts
to generate simpler assertions. Typically, on an access t[i] where t has size 10, RTE will
generate two assertions 0 <= i and i < 10, instead of \valid (&t[i]).
The kernel option -safe-arrays (or -unsafe-arrays) influences the annotations that are gen-
erated for an access to a multi-dimensional array, or to an array embedded in a struct. Option
-safe-arrays, which is set by default in Frama-C, requires that all syntactic accesses to such
an array remain in bound. Thus, if the field t of the struct s has size 10, the access s.t[i] will
generate an annotation i < 10, even if some fields exist after t in s.1 Similarly, if t is declared
as int t[10][10], the access t[i][j] will generate assertions 0 <= i < 10 and 0 <= j < 10,
even though t[0][12] is also t[1][2].
Finally, dereferencing a pointer to a functions leads to the emission of a \valid_function
predicate, to protect against a possibly invalid pointer (ISO C99 6.3.2.3:8). Those assertions
are generated provided option -rte-pointer-call is set.

Example 2.3 An example of RTE annotation generation for checking the validity of each
memory access:
  1
    Thus, by default, RTE is more stringent than the norm. Use option -unsafe-arrays if you want to
allow code such as s.t[12] in the example above.




                                                15
                       CHAPTER 2. RUNTIME ERROR ANNOTATION GENERATION



     extern void f(int* p);
     int i;
     unsigned int j;

     int main(void) {
        int *p;
        int tab[10];

        /*@ assert rte: mem_access: \valid(p); */
        *p = 3;

        /*@ assert rte: index_bound: 0 <= i; */
        /*@ assert rte: index_bound: i < 10; */
        /*@ assert rte: mem_access: \valid_read(p); */
        tab[i] = *p;

        /*@ assert rte: mem_access: \valid(p+1); */
        /*@ assert rte: index_bound: j < 10; */
        // No annotation 0 <= j, as j is unsigned
        *(p + 1) = tab[j];

        return 0;
    }


Example 2.4 An example of memory access validity annotation generation for structured
types, with options -safe-arrays and -rte-pointer-call set.
    struct S {
        int val;
        struct S *next;
    };

    struct C {
        struct S cell[5];
        int (*f)(int);
    };

    struct ArrayStruct {
        struct C data[10];
    };

     unsigned int i, j;

     int main() {
        int a;
        struct ArrayStruct buff;
       // some code

        /*@   assert rte: index_bound: i < 10; */
        /*@   assert rte: index_bound: j < 5; */
        /*@   assert rte: mem_access: \valid_read(&(buff.data[i].cell[j].next)->val); */
        a =   (buff.data[i].cell[j].next)->val;

        /*@ assert rte: index_bound: i < 10; */
        /*@ assert rte: function_pointer: \valid_function(buff.data[i].f); */
        (*(buff.data[i].f))(a);




                                             16
                            2.3. UNSIGNED OVERFLOW ANNOTATIONS




          return 0;
      }

Notice the annotation generated for the call (*(buff.data[i].f))(a).


2.3       Unsigned overflow annotations

ISO C99 states that unsigned integer arithmetic is modular: overflows do not occur (para-
graph 6.2.5.9 of ISO C99). On the other hand, most first-order solvers used in deductive ver-
ification (excluding dedicated bit-vector solvers such as [1]) either provide only non-modular
arithmetic operators, or are much more efficient when no modulo operation is used besides
classic full-precision arithmetic operators. Therefore RTE offers a way to generate asser-
tions preventing unsigned arithmetic operations to overflow (i.e. involving computation of a
modulo).
Operations which are considered by RTE regarding unsigned overflows are addition, subtrac-
tion, multiplication. Negation (unary minus), left shift. and right shift are not considered.
The generated assertion requires the result of the operation (in non-modular arithmetic) to
be less than the maximal representable value of its type, and nonnegative (for subtraction).

Example 2.5
The following file only contains unsigned arithmetic operations: no assertion is generated by
RTE by default.
      unsigned int f(unsigned int a, unsigned int b) {
        unsigned int x, y;
        x = a * (unsigned int)2;
        y = b - x;
        return y;
      }

To generate assertions w.r.t. unsigned overflows, options -warn-unsigned-overflow must be
used. Here is the resulting file on a 32 bits target architecture (-machdep x86_32):
      unsigned int f(unsigned int a, unsigned   int b) {
        unsigned int x, y;
        /*@ assert rte: unsigned_overflow:      0 <= a*(unsigned int)2; */
        /*@ assert rte: unsigned_overflow:      a*(unsigned int)2 <= 4294967295; */
        x = a * (unsigned int)2;
        /*@ assert rte: unsigned_overflow:      0 <= b-x; */
        /*@ assert rte: unsigned_overflow:      b-x <= 4294967295; */
        y = b - x;
        return y;
      }



2.4       Unsigned downcast annotations

Downcasting an integer type to an unsigned type is a well-defined behavior, since the value
is converted using a modulo operation just as for unsigned overflows (ISO C99 paragraph
6.3.1.3.2). The RTE plug-in offers the possibility to generate assertions preventing such
occurrences of modular operations with the -warn-unsigned-downcast option.




                                                17
                      CHAPTER 2. RUNTIME ERROR ANNOTATION GENERATION



Example 2.6
On the following example, the sum of two int is returned as an unsigned char:
      unsigned char f(int a, int b) {
        return a+b;
      }

Using RTE with the -warn-unsigned-downcast option gives the following result:
      unsigned char f(int a, int b) {
        unsigned char __retres;
        /*@ assert rte: unsigned_downcast: a+b <= 255; */
        /*@ assert rte: unsigned_downcast: 0 <= a+b; */
        /*@ assert rte: signed_overflow: -2147483648 <= a+b; */
        /*@ assert rte: signed_overflow: a+b <= 2147483647; */
        __retres = (unsigned char)(a + b);
        return (__retres);
      }



2.5     Cast from floating-point to integer types

Casting a value from a real floating type to an integer type is allowed only if the value
fits within the integer range (ISO C99 paragraph 6.3.1.4), the conversion being done with
a truncation towards zero semantics for the fractional part of the real floating value. The
RTE plug-in generates annotations that ensure that no undefined behavior can occur on such
casts.
      int f( float v) {
         int i = (int)(v+3.0f);
         return i;
      }

Using RTE with the -rte- float -to-int option, which is set by default, gives the following
result:
      int f( float v) {
         int i;
        /*@ assert rte: float_to_int: v+3.0f < 2147483648; */
        /*@ assert rte: float_to_int: -2147483649 < v+3.0f; */
        i = (int)(v + 3.0f);
         return i;
      }



2.6     Expressions not considered by RTE

An expression which is the operand of a sizeof (or __alignof, a Gcc operator parsed by Cil)
is ignored by RTE, as are all its sub-expressions. This is an approximation, since the operand
of sizeof may sometimes be evaluated at runtime, for instance on variable sized arrays: see
the example in ISO C99 paragraph 6.5.3.4.7. Still, the transformation performed by Cil on
the source code actually ends up with a statically evaluated sizeof (see the example below).
Thus the approximation performed by RTE seems to be on the safe side.




                                             18
                                              2.7. INITIALIZATION



Example 2.7          Initial source code:
        #include <stddef.h>

        size_t fsize3(int n) {
          char b[n + 3]; // variable length array
          return sizeof b; // execution time sizeof
        }

        int main() {
           return fsize3(5);
        }

Output obtained with frama-c -print with gcc preprocessing:
        typedef unsigned long size_t;
        /* compiler builtin:
           void *__builtin_alloca(unsigned int); */
        size_t fsize3(int n)
        {
          size_t __retres;
          char *b;
          unsigned int __lengthofb;
          {
            /*undefined sequence*/
            __lengthofb = (unsigned int)(n + 3);
            b = (char *)__builtin_alloca(sizeof(*b) * __lengthofb);
          }
          __retres = (unsigned long)(sizeof(*b) * __lengthofb);
          return __retres;
        }

        int main(void)
        {
           int __retres;
          size_t tmp;
          tmp = fsize3(5);
          __retres = (int)tmp;
           return __retres;
        }



2.7        Initialization

Reading an uninitialized value can be an undefined behavior in the two following cases:

  • ISO C11 6.3.2.12 a variable whose address is never taken is read before being initialized,

  • a memory location that has never been initialized is read and it happens that it was a
    trap representation for the type used for the access.

More generally, reading an uninitialized location always results in an indeterminate value
(6.7.9.10). Such a value is either an unspecified value or a trap representation. Only reading
  2
      here we use C11 as it is clearer than C99




                                                      19
                      CHAPTER 2. RUNTIME ERROR ANNOTATION GENERATION



a trap representation is an undefined behavior (6.2.6.1.5). It corresponds to the second case
above.
However for (most) types that do not have trap representation, reading an unspecified value
is generally not a desirable behavior. Thus, RTE is stricter than the ISO C on many aspects
and delegates one case of undefined behavior to the use of compiler warnings. We now detail
the chosen tradeoff.
If a value of a fundamental type (integers, floating point types, pointers, or a typedef of
those) is read, it must be initialized except if it is a formal parameter or a global variable.
We exclude formal parameters as its initialization status must be checked at the call point
(RTE generates an annotation for this). We exclude global variables as they are initialized by
default and any value stored in this variable must be initialized (RTE generates an annotation
for this).
As structures and unions never have trap representation, they can (and they are regularly)
be manipulated while being partially initialized. Consequently, RTE does not require initial-
ization for reads of a full union or structure (while reading fields with fundamental types is
covered by the previous paragraph). As a consequence, the case of structures and unions
whose address is never taken, and being read before being initialized is not covered by RTE.
It is worth noting that this particular case is efficiently detected by a compiler warning (see
-Wuninitialized on Gcc and Clang for example) as it only requires local reasoning that is
easy for compilers (but not that simple to express in ACSL).
If you really need RTE to cover the previous case, please contact the Frama-C team.
Finally, there are some cases when reading uninitialized values via a type that cannot have
trap representation (for example unsigned char ) should be allowed, for example writing a
memcpy function. In this case, one can exclude this function from the range of function anno-
tated with initialization properties by removing them from the set of functions to annotate
(e.g. -rte-initialized="@all,-f"). Note that the excluded functions must preserve the
initialization of globals, as no assertions are generated for them.


2.8    Undefined behaviors not covered by RTE

One should be aware that RTE only covers a small subset of all possible undefined behaviors
(see annex J.2 of [4] for a complete list).
In particular, undefined behaviors related to the following operations are not considered:

  • Use of relational operators for the comparison of pointers that do not point to the same
    aggregate or union (ISO C99 6.5.8)

  • Demotion of a real floating type to a smaller floating type producing a value outside of
    the representable range (ISO C99 6.3.1.5)

  • Conversion between two pointer types produces a result that is incorrectly aligned
    (ISO C99 6.3.2.3)




                                              20
                                                                            Chapter 3

                                                            Plug-in Options

Enabling RTE plug-in is done by adding -rte on the command-line of Frama-C. The plug-in
then selects every C function which is in the set defined by the -rte-select: if no explicit
set of functions is provided by the user, all C functions defined in the program are selected.
Selecting the kind of annotations which will be generated is performed by using other RTE
options (see fig. 3.1 and 3.2 for a summary).
Pretty-printing the output of RTE and relaunching the plug-in on the resulting file will
generate duplicated annotations, since the plug-in does not check existing annotations before
generation. This behaviour does not happen if RTE is used in the context of a Frama-C
project [5]: the annotations are not generated twice.




                                             21
                                 CHAPTER 3. PLUG-IN OPTIONS




Option                           Type (Default)                      Description
-warn-unsigned-overflow          boolean (false)                     Generate annotations for
                                                                     unsigned overflows
-warn-unsigned-downcast          boolean (false)                     Generate annotations for
                                                                     unsigned integer downcast
-warn-signed-overflow            boolean (true)                      Generate annotations for
                                                                     signed overflows
-warn-signed-downcast            boolean (false)                     Generate annotations for
                                                                     signed integer downcast
-warn-pointer-downcast           boolean (true)                      Generate annotations for
                                                                     downcast of pointer values
-warn-left-shift-negative        boolean (true)                      Generate annotations for
                                                                     left shift on negative values
-warn-right-shift-negative       boolean (false)                     Generate annotations for
                                                                     right shift on negative val-
                                                                     ues
-warn-invalid-bool               boolean (true)                      Generate annotations for
                                                                     _Bool trap representations
-warn-special-float              string: (non-finite), nan or none   generate annotations when
                                                                     special floats are produced:
                                                                     infinite floats or NaN (by
                                                                     default), only on NaN or
                                                                     never.
-warn-invalid-pointer            boolean (false)                     Generate annotations for
                                                                     invalid pointer arithmetic

                      Table 3.1: Frama-C kernel options, impacting RTE


Option                        Type (Default)           Description
-rte                          boolean (false)          Enable RTE plug-in
-rte-div                      boolean (true)           Generate annotations for division by zero
-rte- float -to-int           boolean (true)           Generate annotations for casts from
                                                       floating-point to integer
-rte-initialized              set of function (none)   Generate annotations for initialization for
                                                       the given set of functions
-rte-mem                      boolean (true)           Generate annotations for validity of left-
                                                       values access
-rte-pointer-call             boolean (true)           Generate annotations for validity of calls
                                                       via function pointers
-rte-shift                    boolean (true)           Generate annotations for left and right
                                                       shift value out of bounds
-rte-select                   set of function (all)    Run plug-in on a subset of C functions
-rte-trivial-annotations      boolean (false)          Generate all annotations even when they
                                                       trivially hold
-rte-warn                     boolean (true)           Emit warning on broken annotations

                                   Table 3.2: RTE options




                                               22
                                      BIBLIOGRAPHY




                                                               Bibliography

[1] Armin Biere. Boolector. http://fmv.jku.at/boolector/.

[2] Loïc Correnson, Zaynah Dargaye, and Anne Pacalet. WP plug-in Manual. CEA List,
    Software Reliability Laboratory.

[3] Pascal Cuoq, Boris Yakobowski, and Virgile Prevosto. Frama-C’s value analysis plug-
    in. CEA List, Software Reliability Laboratory. http://frama-c.com/download/
    frama-c-eva-manual.pdf.

[4] International Organization for Standardization (ISO). The ANSI C standard (C99). http:
    //www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf.

[5] Julien Signoles with Loïc Correnson, Matthieu Lemerre and Virgile Prevosto. Plug-in
    Development Guide. CEA List, Software Reliability Laboratory.




                                           23