Static Analysis Errors

The following are samples of the kinds of errors detected by static analysis.

Example: Bounds Violation

This is an example of an off-by-one loop that will write outside the bounds of two array variables: before the start of one, and after the end of the other.

1  int a[10], b[10];
2
3  void f() {
4     int i;
5     for (i = 0; i <= 10; i++) {
6        a[i] = 0;     // will assign a[10] (bounds error)
7        b[i – 1] = 0; // will assign b[-1] (bounds error)
8     }
9  }

Example: Data Race

The example below shows a basic OpenMP* parallel for loop. The #pragma omp indicates that the iterations of this loop are intended to run in parallel. The reduction clause on line 7 causes the variable "sum" to be replaced in the body of the loop with a per-thread temporary. The values of all these temporaries are added together and assigned to the outer variable named sum at the end of the loop.

The loop iterations always run in order in serial mode. If the same memory location is written on one iteration and read on another, there is no guarantee in parallel mode the read and write will happen in the same order as they did in serial mode. If the same memory location is written in two iterations, then the final value depends on which writes last. These usage patterns are called loop-carried data dependencies. Their presence implies that the order of execution of independent threads can affect the meaning of the application - an error condition called a data race.

The example below contains two possible data dependencies related to the variable b. On line 9, the a[i]-th element of b is written, and on line 10, the i-th element of b is read. Depending on the values in the array a, this might mean that the same array element is read in one iteration and written in another. It could also mean that the same value is written in two iterations on line 9. Note that the variable "sum" would create an unconditional loop-carried data dependency, but the reduction clause removes this problem because each thread has its own copy of sum.

In this particular case, static analysis cannot know for sure whether a data race actually exists or not. For example, if a[i] were equal to i (or equal to zero) for all values of i between 1 and 99, then no data race would exist. Therefore these issues are reported as possible data race errors.

1  #include <omp.h>
2  int a[100], b[100];
3  void do_work()
4  {
5     int i, sum = 0;
6
7     #pragma omp parallel for reduction(+:sum)    
8     for (i = 1; i < 100; i++) {
9        b[a[i]] = i;      // possible write-write race on b
10       sum = sum + b[i]; // possible read-write race on b; see line 9
11    }
12
13    printf("sum = %d\n", sum);
14 }

This same kind of parallel application (with the same data races) can be coded using Intel® Cilk™ Plus methodology as shown below. Here the reduction clause is replaced by a hyperobject. The same error would also be detected during static analysis.

1  #include <cilk/cilk.h>
2  #include <cilk/reducer_opadd.h>
3  int a[100], b[100];
4  void do_work()
5  {
6     cilk::reducer_opadd<int> sum(0);     
7         
8     cilk_for (int i = 1; i < 100; i++) {
9        b[a[i]] = i;      // possible write-write race on b
10       sum = sum + b[i]; // possible read-write race on b; see line 9
11    }
12
13    printf("sum = %d\n", sum.get_value());
14 }

Example: Tainted Variable Usage

Many security issues are caused by failure to adequately screen user input. Static analysis regards values that come into the application from outside (such as from input statements or command line parameters) as suspicious or tainted. Static analysis flags cases where tainted data is used in a dangerous way without prior examination. Here is an example:

1 #include <stdio.h>
2 int main(int argc, char **argv) {
3    int upper, i;
4  
5    upper = atoi(argv[1]); // upper is a tainted value
6
7    // upper is an unchecked value so
8    // this loop could run a LONG time
9    for (i = 0; i < upper; i++) {
10       printf("i = %d\n", i);
11   }
12
13   return 0;
14 }

Note that static analysis cannot determine whether a specific check is adequate to make a tainted value safe. Just about any kind of check removes the taint and causes static analysis to allow it to be used freely. For example, this application really is no better than the one above, but it would not be flagged with a tainted variable error. A better check would be if (upper < 1000).

1 #include <stdio.h>
2 int main(int argc, char **argv) {
3    int upper, i;
4  
5    upper = atoi(argv[1]); // upper is a tainted value
6
7    if (upper > 0) {
8       // this check is inadequate; loop is still unsafe
9       // but no diagnostic would be issued
10      for (i = 0; i < upper; i++) {
11         printf("i = %d\n", i);
12      }
13   }
14
15   return 0;
16 }

See Also


Supplemental documentation specific to a particular Intel Studio may be available at <install-dir>/<studio>/documentation/.

For more complete information about compiler optimizations, see our Optimization Notice.