Описание способов анализа кода с помощью утилиты bayzr

bayzr получает информацию о проверяемых файлах на основе вывода команды сборки проекта или компиляции файла(ов). bayzr анализирует вывод, формирует список файлов и список макроопределений и путей подключения заголовочных файлов. Все эти собранные определения и список файлов передаются анализатору.

Анализаторы бывают нескольких типов:

  1. самостоятельно собирающие данные о компилируемых файлах: clang-analyzer, make [группа 1]
  2. несамостоятельные анализаторы. т.е те, которым необходимо передать список определений для более точного анализа проекта, но допускающие отсутствие таких параметров: cppcheck, rats [группа 2]
  3. несамостоятельные анализаторы. т.е те, которым необходимо передать список определений для более точного анализа проекта и строго требующие параметры для анализа:splint, oclint [группа 3]
  4. анализаторы с графическим интерфейсом, которые позволяют провести дальнейший анализ кода: frama-c [группа 4]

А теперь рассмотрим способы анализа проектов bayzr утилитой.

Способ подходит для анализаторов группы 2, но в этом случае, параметры для более точного анализа файлов необходимо формировать самостоятельно:

Пример команды для нахождения всех файлов с расширением c. К каждому найденному файлу добавляется строка с gcc, таким образом можно сформировать необходимые -D и -I:

bayzr cmd "find . -name '*.c' | xargs -n 1 echo 'gcc '"

[test@localhost check]$ bayzr cmd "find . -name '*.c' | xargs -n 1 echo 'gcc '"; cat report_buggy_c.txt
--------------------Process of gathering source information is begun-----------------
gcc  ./test.c
--------------------Process of source analyzing is begun-----------------------------
--------------------Process of source analyzing is begun by plugin cppcheck----------------
Analyzer error: stderr scanner error read |0: bad file descriptor
Checking /home/test/check/test.c...
/home/test/check/test.c|9|warning|invalidPrintfArgType_sint|%d in format string (no. 1) requires 'int' but the argument type is 'signed long'.
/home/test/check/test.c|25|error|unknownEvaluationOrder|Expression '++a+a++' depends on order of evaluation of side effects
/home/test/check/test.c|24|error|arrayIndexOutOfBounds|Array 'c[20]' accessed at index 20, which is out of bounds.
/home/test/check/test.c|27|error|negativeIndex|Array index -1 is out of bounds.
/home/test/check/test.c|15|error|bufferAccessOutOfBounds|Buffer is accessed out of bounds.
/home/test/check/test.c|14|warning|allocaCalled|Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead.
/home/test/check/test.c|10|error|memleak|Memory leak: a
/home/test/check/test.c|22|error|uninitvar|Uninitialized variable: i
/home/test/check/test.c|36|error|uninitvar|Uninitialized variable: buf
/home/test/check/test.c

         4|  #include <string.h>
         5|   
         6|  int function1(char *a){
         7|      printf("Variable 1 = %d\n", strlen(a));
         8|      a = malloc(10*sizeof(char));
//**DETECT** cppcheck:      %d in format string (no. 1) requires 'int' but the argument type is 'signed long'.
         9|      sprintf(a, "This is huge text, more then array size %d\n", 10000000000L);
//**DETECT** cppcheck:      Memory leak: a
        10|      return strlen(a);
        11|  }
        12|   
        13|  char *function2(){
//**DETECT** cppcheck:      Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead.
        14|      char *internal_buffer = alloca(10 * sizeof(char));
//**DETECT** cppcheck:      Buffer is accessed out of bounds.
        15|      snprintf(internal_buffer, 12, "Another big string to copy to the buffer\n");
        16|      return internal_buffer;
        17|  }
        18|   
        19|  int function3(int a, int b){
        20|      int c[20];
        21|      int i;
//**DETECT** cppcheck:      Uninitialized variable: i
        22|      a = a * i;
        23|      for (i=0;i<21;i++){
//**DETECT** cppcheck:      Array 'c[20]' accessed at index 20, which is out of bounds.
        24|       c[i]=a*b;
//**DETECT** cppcheck:      Expression '++a+a++' depends on order of evaluation of side effects
        25|       a=++a+a++;
        26|      }
//**DETECT** cppcheck:      Array index -1 is out of bounds.
        27|      return c[i-22];
        28|  }
        29|   
        30|  int main(int argc, char **argv){
        31|      char arr[10] = "12345678901";
        32|      char *buf;
        33|      if (argc>0){
        34|      strcpy(arr, argv[1]);
        35|      }
//**DETECT** cppcheck:      Uninitialized variable: buf
        36|      int result = function1(buf);
        37|      printf("Result %d, %s\n", result, arr);
        38|      buf = function2();
        39|      printf("Buf %s\n", buf);
        40|      result = function3(10,20);
        41|      printf("Result %d\n", result);
.......

Таким образом можно список файлов для анализа и параметры сборки собрать в отдельный файл и сделать cat check_files.txt

Способ подходит для всех типов анализаторов группы от 1 до 4.

типичный пример использования:

[#]$ ./configure
[#]$ bayzr cmd make VERBOSE=1

В текущем примере используется VERBOSE=1 для раскрытия утилитой make процесса сборки, хоя возможно обойтись и без этого, а с помощью настройки: replace = on в локальном или глобальном bzr.conf. В этом случае переменные окружения CC и CXX будут подменены на bayzr и параметры компиляции будут перехвачены даже при отключенном выводе процесса сборки.

Режим подобен предыдущему, но в отчет попадают команды, которые были сформированы для вызова анализатора статического кода. Такой режим полезен в случае, когда возможностей bayzr не хватает и нужны дополнительные настройки. Например для анализатора группы 4, frama-c имеет графический анализатор, позволяющий провести дополнительный анализ исходных файлов.

Команды сформированные bayzr можно напрямую вызвать из командной строки с frama-c-gui например.

[test@localhost check]$ bayzr -debug-commands --dry-run cmd gcc -o buggy test.c
--------------------Process of gathering source information is begun-----------------
test.c: In function ‘main’:
test.c:31:20: warning: initializer-string for array of chars is too long [enabled by default]
     char arr[10] = "12345678901";
                    ^
--------------------Process of source analyzing is begun-----------------------------
--------------------Process of source analyzing is begun by plugin frama-c----------------
/usr/bin/frama-c -no-frama-c-stdlib -cpp-gnu-like -val -va -wp -sparecode -security-slicing -nonterm -cpp-extra-args="   -Dlinux  -IcHeaderFileGeneratedByBzrPrg.h -I/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include -I/usr/local/include -I/usr/include -I. -m64 -mtune=generic"  test.c
[test@localhost check]$ /usr/bin/frama-c-gui -no-frama-c-stdlib -cpp-gnu-like -val -va -wp -sparecode -security-slicing -nonterm -cpp-extra-args="   -Dlinux  -IcHeaderFileGeneratedByBzrPrg.h -I/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include -I/usr/local/include -I/usr/include -I. -m64 -mtune=generic"  test.c

Параметр -dry-run позволяет не запускать анализатор статического кода, а просто выводит сформированные команды для запуска анализатора

Подходит для всех типов анализаторов группы от 1 до 4, анализ проходят только по тем файлам, которые выбраны для отчета.

[test@localhost mod_performance-master]$ bayzr -files chart.c cmd make
[test@localhost mod_performance-master]$ cat report.txt 
/home/test/mod_performance-master/chart.c

       147|        (*x)[k] = 0;
       148|        (*y)[k] = 0;
       149|      }
       150|      
       151|      char *bug = malloc(20);
//**DETECT** cppcheck:      Buffer is accessed out of bounds.
       152|      strcpy(bug, "ASDFGHJKLQWERTYUIOPHJKUYTF");
//**DETECT** cppcheck:      Memory leak: bug
       153|      return;
       154|  
       155|  }
       156|  
       157|  void
       158|  readmatrix(apr_array_header_t *data, double **a, double **b, double **x,
.......
       466|          }
       467|        if (s->date > cur)
       468|          return 0.0;
       469|      }
       470|    char a[5]="FRTDESWERTYUUU";
//**DETECT** cppcheck:      %d in format string (no. 1) requires 'int' but the argument type is 'char *'.
       471|    printf("%d",a);
       472|    return 0.0;
       473|  }
       474|  
       475|  apr_array_header_t *
       476|  chart_fill_data(apr_array_header_t *data, int min, int max)
.......
       762|          }
       763|        if (ti.seconds)
       764|          {
       765|            sprintf(buffer, "sec %d", ti.seconds);
       766|            gdImageString(im, fptr, rw - 2 * dx - 10, rh - dup, (unsigned char *)buffer, black);
//**DETECT** cppcheck:      Variable 'dup' is assigned a value that is never used.
       767|            dup -= 10;
       768|          }
       769|      }
       770|    else
       771|      {
       772|        gdImageLine(im, rw - 2 * dx, rh - 40, rw - dx, rh - 40, black);
.......

или вообще, список заданных файлов берется из патч файла:

[test@localhost mod_performance-master]$ cat new-commit.patch 
diff --git a/chart.c b/chart.c
index 81470f4..9835b81 100644
--- a/chart.c
+++ b/chart.c
@@ -147,6 +147,10 @@ allocmatrix(apr_pool_t *p, double **a, double **b, double **x, double **y,
       (*x)[k] = 0;
       (*y)[k] = 0;
     }
+    
+    char *bug = malloc(20);
+    strcpy(bug, "ASDFGHJKLQWERTYUIOPHJKUYTF");
+    return;
 
 }
 
[test@localhost mod_performance-master]$ bayzr -diff new-commit.patch cmd make
[test@localhost mod_performance-master]$ cat report.txt 
/home/test/mod_performance-master/chart.c

       147|        (*x)[k] = 0;
       148|        (*y)[k] = 0;
       149|      }
       150|      
       151|      char *bug = malloc(20);
//**DETECT** cppcheck:      Buffer is accessed out of bounds.
       152|      strcpy(bug, "ASDFGHJKLQWERTYUIOPHJKUYTF");
//**DETECT** cppcheck:      Memory leak: bug
       153|      return;
       154|  
       155|  }
       156|  
       157|  void
       158|  readmatrix(apr_array_header_t *data, double **a, double **b, double **x,
.......