Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
itc-benchmarks
==============
This is a modified version of the
[itc-benchmarks Github](https://github.com/regehr/itc-benchmarks)
repository, with tests from the Toyota InfoTechnology Center,
made available online by John Regehr.
Several patches have been applied to the original sources to deal with:
- Issues found in the original code
(e.g. copy-paste typos and undefined behaviors (UBs));
- Recursive calls (commented out for running Eva);
- Portability issues (e.g. casts `pthread_t -> int`).
## Renamings
- The `.` in directory names was replaced with `_`
to avoid issues with analysis scripts;
- `02_wo_Defects/free_nondynamically_allocated_memory.c` was renamed to
`02_wo_Defects/free_nondynamic_allocated_memory.c`, to match the equivalent
file in `01_w_Defects`.
Tests out of scope
==================
Concurrency and recursive calls are not yet handled by Eva.
Therefore, all tests whose defects concern threads and concurrency are not
handled by Eva.
Also, some kinds of defects that do not imply undefined behavior, such as
redundant conditions in tests, are out of the scope of Frama-C/Eva.
However, such tests are still left in the code, since they allow us to detect
unexpected undefined behaviors (e.g. as detailed later for `redundant_code.c`).
Applied patches
===============
The list of applied patches is separated into three categories:
- Likely unintentional typos;
- Unforeseen undefined behaviors;
- Limitations for Eva.
The first category comprises errors which look like simple unintended typos.
The second contains some code that invokes undefined behavior, but does not
seem caused by a simple typo.
Finally, the last category contains mostly commented out calls to recursive
functions.
## Typos
- `buffer_underrun_dynamic.c`, function `dynamic_buffer_underrun_037`:
doubleptr[0][0]='T'; // the first '0' should have been 'i'
- `littlemem_st.c`, functions `littlemem_st_008_func_002`,
`littlemem_st_009_func_002`, `littlemem_st_010_func_002` and
`littlemem_st_011_func_002`: the same copy-paste error was made in these
four functions, in which the global variable name copied from function 007
was not updated. This results in code referring to a different variable,
leading to possible runtime errors during execution. Fixing each reference
to the corresponding global variable (as was clearly intended in the code)
prevents false alarms.
- `redundant_code.c`, function `redundant_cond_009`:
The comment before the function suggests the loop condition, in the
`02_wo_Defects` version, should be `a > 10` (or `10 < a`).
This is also closer to the code in the `01_w_Defects` version.
Interestingly, the inversion leads to undefined behavior, since the
value of `a` will cycle through all negative values, before being
decremented and wrapping around, causing a signed overflow.
Fixing the condition is necessary to prevent UB in the `02_wo_Defects`
version.
## Undefined behavior
- `data_lost.c`, function `data_lost_006`, and also `data_overflow`, functions
`data_overflow_024` and `data_overflow_025`:
Float to int conversions which overflow (larger than the maximum integer
value for the corresponding type) are undefined behavior. While the UB is
deliberate in the `01_w_Defects` directory, there is also UB in the
`02_wo_Defects` directory. The code was patched to a value that fits in the
integer type.
- `invalid_memory_access.c`, function `invalid_memory_access_003`:
A char pointer `c` is passed to `malloc` then `free`, and then assigned to
another pointer (`psink = c`). This last assignment (after `c` has been
freed) entails the access to an indeterminate value (the dangling pointer),
which Eva detects as UB. It is not necessary to keep the assignment in the
`01_w_Defects` directory, since there is already an UB earlier in the code.
The instruction was removed from both versions of the file.
- `overrun_st.c`, several functions `overrun_st_XXX`:
These functions all follow the same pattern, which results in accessing an
uninitialized variable. As an example, here is the code of `overrun_st_001`
(without defects):
void overrun_st_001 ()
{
char buf[5];
buf[4] = 1;
sink = buf[idx];
}
`idx` is a global variable, default-initialized to 0. Therefore, the last
statement, `sink = buf[idx]` (likely introduced to avoid compiler warnings
about "unused variable buf"), will access uninitialized memory. This
situation happens 37 times in the file. A simple patch in all cases
consists in 0-initializing the arrays by adding ` = {0}` to their
declarations. this removes the unintentional errors in the `02_wo_defects`
version, without removing the intended errors in `01_w_defects`.
- `st_overflow.c`, function `st_overflow_006_func_001`:
Same issue as previously: a local array without initializer has its element
accessed, even in the `02_wo_defects` version. The same solution is applied:
the array is 0-initialized by adding `= {0}` to its declaration.
- `bit_shift.c`, function `bit_shift_009`:
The code performs a left-shift of a signed value, `1 << (rand() % 32)`.
However, `1 << 31` (the maximum possible value) has type `signed int`.
In a 32-bit architecture (as is assumed for this set of benchmarks),
this leads to a signed overflow, which is undefined behavior.
The patch consists in replacing `rand() % 32` with `rand() % 31`.
- `st_underrun.c`, functions `st_underrun_002_func_001` and
`st_underrun_007_func_001` (only the `02_wo_defects` version):
The no-defects versions of these functions actually still contain some
undefined behavior. This is the code of `st_underrun_002_func_001`:
void st_underrun_002_func_001 (st_underrun_002_s_001 s) {
int len = strlen(s.buf) - 1;
for (;s.buf[len] != 'Z';len--) {
if ( len < 0 ) break;
}
}
`s.buf` initially contains `"STRING"` (therefore no 'Z's).
When `len` reaches 0, the test inside the loop is still false, so
we return to the loop decrement, `len` becomes -1, and then we
test `s.buf[-1] != 'Z'`. This causes an out-of-bounds access,
leading to undefined behavior.
The code likely intended to return -1 when the character is not
found, but since the value of `len` is not actually used here, we
can simply replace the inner test with `len <= 0` to avoid the
undefined behavior.
The same patch is applicable to `st_underrun_007_func_001`.
- `st_underrun.c`, function `st_underrun_007`:
This function also suffers from the uninitialized access issue mentioned
in file `overrun_st.c`: variable `st_underrun_007_s_001` only has the first
element of array `buf` initialized (to 1), while the remaining of the array
is left uninitialized. When `st_underrun_007_func_001` is called, there is
an access to the uninitialized values of `s.buf` inside `strlen`.
The same patch as for `overrun_st.c` (adding `= {0}` to the declaration)
also works here.
- `ptr_subtraction.c`, function `ptr_subtraction_001`:
The no-defects version contains a subtraction between pointers of different
bases (`buf1 - buf2`) which itself leads to undefined behavior. It is
necessary to comment out this instruction (and the one using its result).
## Limitations for Eva
Eva does not handle recursive calls yet, so a few tests containing them had
the recursive calls commented out:
- `invalid_memory_access.c`, function `invalid_memory_access_005`;
- `st_overflow.c`, function `st_overflow_005_func_001`;
- `st_underrun.c`, function `st_underrun_005`.
Also, 2 test files had deliberate parsing errors (concerning function pointers)
which prevent Frama-C from parsing them. They were removed from the list of
files to be parsed and their functions commented out from `main`.
Also, 5 tests concerning concurrency were removed because their source files
perform a non-portable cast from `pthread_t` to `int` (POSIX states that
the type of `pthread_t` is not required to be an arithmetic type. Frama-C's
libc defines it with a `struct`, which is allowed by the standard, and chosen
to deliberately help identify such non-conformance violations.
Lastly, since the tests are out of the scope of Eva anyway, removing them has
no impact in the end.
Remaining alarms
================
A few alarms remain in the code, due to:
- Imprecision in the handling of the variadic function `snprintf`:
this function assigns to a buffer, which is then passed to `strlen`.
However, there is currently no way to emulate the precise behavior of
what `snprintf` produces, therefore we use a safe approximation in which the
string is possibly non-terminated. This leads to 2 alarms (one in the caller
and another in the callee).
- Signed overflows in `02_wo_Defects/redundant_code.c`, functions
`redundant_cond_009` and `redundant_cond_0013`:
Even after fixing the loop condition as mentioned previously,
Eva is not able to infer that there is no signed overflow in the loop.
This is because the value of `a` is imprecise (the range `[10..32767]`
inside the loop) and very large, so a huge number of iterations would
be necessary to obtain a precise result (that is, that the sum
`32767 + 32766 + ... + 10`, stored in `b`, does not overflow a 32-bit int).
Frama-C/Eva performs a widening which leads to a spurious alarm about
the signed overflow.
"False negatives"
=================
A few test cases in the `01_w_Defects` do not lead to alarms in Frama-C/Eva.
These test cases are related to situations in which, from the point of view
of the user, it might make sense to consider them as "defects", but from the
point of view of the C standard, they are perfectly valid situations.
One such case is in file `memory_allocation_failure.c`, function
`memory_allocation_failure_002`:
long long int *ptr=(long long*) malloc(MAX_VAL *sizeof(long long));
`MAX_VAL` is defined as `UINT_MAX`, therefore an unsigned integer type.
The test intention was that the size should "overflow".
However, unsigned overflow is allowed by the C standard, therefore
Frama-C by default does not raise any alarms.
It is possible to use option `-warn-unsigned-overflow` to trigger an alarm
in this situation, but then the rest of the code needs to be patched to avoid
triggering it in other situations.
In particular, the same file `memory_allocation_failure.c` contains the
following variable declaration:
static unsigned int static_var = MAX_VAL*2;
Which makes the analysis stop during global variable initialization, since a
definite alarm has been found.
Fixing such issues should allow Frama-C to detect the overflow in the call
to `malloc`.