to the top
close form
Для получения триального ключа
заполните форму ниже
Team license
Enterprise license
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Бесплатная лицензия PVS-Studio для специалистов Microsoft MVP
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Мне интересно попробовать плагин на:
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
check circle
Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте папку
Spam/Junk и нажмите на письме кнопку "Не спам".
Так Вы не пропустите ответы от нашей команды.

>
>
>
Математикам доверяй, но проверяй

Математикам доверяй, но проверяй

30 Мар 2014

Я временами бываю озадачен, рассматривая ошибки в очередном программном проекте. Многие из этих ошибок живут в проектах годами. Смотришь на сотню ляпов в коде и удивляешься, как программа вообще работает. И ведь как-то работает. Ей даже пользуются. Причем, я говорю не о коде, рисующем покемона в игре. А, например, о математических библиотеках. Да, вы верно догадались. В этой статье пойдет речь о проверке кода математической библиотеки Scilab.

0244_Trust_but_Verify_ru/image1.png

Scilab

Сегодня мы будем рассматривать подозрительные фрагменты кода в математическом пакете Scilab. Анализа выполнен с помощью инструмента PVS-Studio.

Scilab - пакет прикладных математических программ, предоставляющий мощное открытое окружение для инженерных (технических) и научных расчётов [Wikipedia].

Официальный сайт: http://www.scilab.org/

В системе доступно множество инструментов:

  • 2D и 3D графики, анимация;
  • линейная алгебра, разреженные матрицы (sparse matrices);
  • полиномиальные и рациональные функции;
  • интерполяция, аппроксимация;
  • симуляция: решение ОДУ и ДУ;
  • Scicos: гибрид системы моделирования динамических систем и симуляции;
  • дифференциальные и не дифференциальные оптимизации;
  • обработка сигналов;
  • параллельная работа;
  • статистика;
  • работа с компьютерной алгеброй;
  • интерфейс к Fortran, Tcl/Tk, C, C++, Java, LabVIEW.

Приготовьтесь. Статья будет длинная. Я ведь не виноват, что тут так много разных пакостей. И хочется показать разные классы ошибок.

Конечно, найденные недостатки не имеют отношения к математике. Возможно, все алгоритмы в библиотеке правильные и эффективные. Однако, раз для написания выбран язык Си++, надо не забывать, что помимо ошибок в алгоритмах, бывают ещё и опечатки, разыменование нулевых указателей и прочие ляпы. Собственно, пользователю не важно, встретится ему логическая ошибка в численном алгоритме или он станет жертвой неинициализированной переменной.

Да, статический анализ находит только некоторые виды ошибок. Но раз их легко обнаружить, зачем себе отказывать в таком удовольствии. Лучше поправить ещё 10% ошибок, чем вовсе ничего не поправить.

Итак, давайте посмотрим, что рассказал мне анализатор PVS-Studio о проекте Scilab.

Буфер, которого нет

0244_Trust_but_Verify_ru/image2.png

int sci_champ_G(....)
{
  ....
  char * strf = NULL ;
  ....
  if ( isDefStrf( strf ) )
  {
    char strfl[4];
    strcpy(strfl,DEFSTRFN);
    strf = strfl;
    if ( !isDefRect( rect ) )
    {
      strf[1]='5';
    }
  }

  (*func)(stk(l1), stk(l2), stk(l3), stk(l4),
    &m3, &n3, strf, rect, arfact, 4L);
  ....  
}

Сообщение PVS-Studio: V507 Pointer to local array 'strfl' is stored outside the scope of this array. Such a pointer will become invalid. sci_champ.c 103

Ссылка на временный массив 'strfl' сохраняется в переменной 'strf'. При выходе из блока "if () { ... }" этот массив перестаёт существовать. Однако, в программе работают с указателем 'strf'.

Поведение такой программы непредсказуемо. Нельзя работать с массивом, которого уже нет. Конечно, программа может вполне корректно отработать. Однако, это везение. Память, где располагался массив, в любой момент может быть занята под хранение других массивов или переменных.

Аналогичные проблемы:

  • Array 'strfl'. sci_fec.c 111
  • Array 'strfl'. sci_grayplot.c 94
  • Array 'strfl'. sci_matplot.c 84

Что-то не то посчитали

int C2F(pmatj)
  (char *fname, int *lw, int *j, unsigned long fname_len)
{
  ....
  ix1 = il2 + 4;
  m2 = Max(m, 1);
  ix1 = il + 9 + m * n;
  ....
}

Предупреждение PVS-Studio: V519 The 'ix1' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 2387, 2389. stack1.c 2389

С переменной 'ix1' что-то неладно. Мне кажется, здесь какая-то опечатка.

В начале проверка, затем инициализация

0244_Trust_but_Verify_ru/image3.png

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

int sci_Playsound (char *fname,unsigned long fname_len)
{
  ....
  int m1 = 0, n1 = 0;
  ....
  if ( (m1 != n1) && (n1 != 1) ) 
  {
    Scierror(999,_("%s: Wrong size for input argument #%d: ")
                 _("A string expected.\n"),fname,1);
    return 0;
  }
  sciErr = getMatrixOfWideString(pvApiCtx, piAddressVarOne,
             &m1,&n1,&lenStVarOne, NULL);
  ....
}

Предупреждения PVS-Studio: V560 A part of conditional expression is always false: (m1 != n1). sci_playsound.c 66; V560 A part of conditional expression is always true: (n1 != 1). sci_playsound.c 66

Переменные m1 и n1 должны получить значения при вызове функции getMatrixOfWideString(). Потом эти переменные должны быть проверены. Вот только получилось так, что проверка осуществляется до вызова функции getMatrixOfWideString().

В момент проверки переменные m1 и n1 равны 0. Условие "if ( (m1 != n1) && (n1 != 1) )" не выполняется. В результате, поверка никак не влияет на работу программы.

Итого. Не осуществляется проверка переменных m1 и n1.

Магические числа

0244_Trust_but_Verify_ru/image4.png

void CreCommon(f,var)
     FILE *f;
     VARPTR var;
{
  ....
  if ( strncmp(var->fexternal, "cintf", 4)==0 )
  ....
}

Предупреждение PVS-Studio: V666 Consider inspecting third argument of the function 'strncmp'. It is possible that the value does not correspond with the length of a string which was passed with the second argument. crerhs.c 119

Используется магическое число 4. И это число неправильное. В строке "cintf" пять символов, а не четыре. Не используйте такие магические числа.

Я бы сделал специальный макрос для вычисления длины строковых литералов и использовал бы его так:

if ( strncmp(var->fexternal, "cintf", litlen("cintf"))==0 )

Как изготовить макрос 'litlen', обсуждать не будем. Есть масса способов на любой вкус. Главное, избавиться от числа.

Другие неправильные размеры строк:

  • crerhs.c 121
  • crerhs.c 123
  • crerhs.c 125
  • crerhs.c 127

1, 2, 3, 4, 4, 6

0244_Trust_but_Verify_ru/image5.png

int C2F(run)(void)
{
  ....
  static int *Lpt = C2F(iop).lpt - 1;
  ....
  Lpt[1] = Lin[1 + k];
  Lpt[2] = Lin[2 + k];
  Lpt[3] = Lin[3 + k];
  Lpt[4] = Lin[4 + k];
  Lct[4] = Lin[6 + k ];
  Lpt[6] = k;
  ....
}

Предупреждение PVS-Studio: V525 The code containing the collection of similar blocks. Check items '1', '2', '3', '4', '4' in lines 1005, 1006, 1007, 1008, 1009. run.c 1005

Опечатка в последовательности чисел. В результате один элемент массива останется неинициализированным. Можно массу интересных математических результатов получить.

Эволюция кода

0244_Trust_but_Verify_ru/image6.png

int write_xml_states(
  int nvar, const char * xmlfile, char **ids, double *x)
{
  ....
  FILE *fd = NULL;
  ....
  wcfopen(fd, (char*)xmlfile, "wb");
  if (fd < 0)
  {
    sciprint(_("Error: cannot write to  '%s'  \n"), xmlfile);
    ....
}

Предупреждение PVS-Studio: V503 This is a nonsensical comparison: pointer < 0. scicos.c 5826

Я почти уверен, что когда-то в этом коде для открытия файла использовалась функция open. Затем, код переписали и стали использовать функцию на _wfopen. Её вызов спрятан в макрос 'wcfopen'.

А вот проверку, что файл успешно открыт, поправить забыли. Функция open() возвращает в случае ошибки значение -1. Проверять же, что указатель меньше нуля, не имеет никакого практического смысла.

Ещё одно место, где прослеживается история.

void taucs_ccs_genmmd(taucs_ccs_matrix* m,
  int** perm, int** invperm)
{
  int  n, maxint, delta, nofsub;
  ....
  maxint = 32000;
  assert(sizeof(int) == 4);
  maxint = 2147483647; /* 2**31-1, for 32-bit only! */
  ....
}

Предупреждение PVS-Studio: V519 The 'maxint' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 154, 157. taucs_scilab.c 157

Ошибки здесь нет, но код забавен.

Видно, что давным-давно, было написано "maxint = 32000;". Затем ниже появилось:

assert(sizeof(int) == 4);
maxint = 2147483647; /* 2**31-1, for 32-bit only! */

Сортируем один элемент

0244_Trust_but_Verify_ru/image7.png
char *getCommonPart(char **dictionary, int sizeDictionary)
{
  ....
  char *currentstr = dictionary[0];
  qsort(dictionary, sizeof dictionary / sizeof dictionary[0],
        sizeof dictionary[0], cmp);
  ....
}

Предупреждение PVS-Studio: V514 Dividing sizeof a pointer 'sizeof dictionary' by another value. There is a probability of logical error presence. getcommonpart.c 76

Второй аргумент функции qsort() - это количество элементов в массиве. Из-за ошибки, количество элементов всегда равно одному.

Рассмотрим выражение "sizeof dictionary / sizeof dictionary[0]". Здесь размер указателя делится на размер указателя. Результат равен единице.

Наверное, правильный код должен был быть таким:

qsort(dictionary, sizeDictionary, sizeof dictionary[0], cmp);

Аналогичная ошибка здесь: getfilesdictionary.c 105

Упрямые строки

void GetenvB(char *name, char *env, int len)
{
  int ierr = 0, one = 1;
  C2F(getenvc)(&ierr,name,env,&len,&one);
  if (ierr == 0) 
  {
    char *last = &env[len-1];
    while ( *last == ' ' ) { last = '\0' ; } 
    last--;
  }
  ....
}

V527 It is odd that the '\0' value is assigned to 'char' type pointer. Probably meant: *last = '\0'. getenvb.c 24

Эта строка ужасна. Или прекрасна, если мы говорим об интересных ошибках.

while ( *last == ' ' ) { last = '\0' ; }

Если первый символ в строке пробел, то указатель станет равен нулю. Далее возникнет обращение по нулевому указателю.

Мне кажется, этот код должен был заменить все пробелы на '\0'. Тогда код должен быть таким:

while ( *last == ' ' ) { *last++ = '\0' ; }

Забавно, что есть ещё одно место в коде, где тоже хотят менять пробелы на нули. И его тоже не удалось написать правильно.

static int msg_101(int *n, int *ierr)
{
  ....
  for (i=0;i<(int)strlen(line);i++)
  {
    if (line[i]==' ') line[i]='\0';
    break;
  }
  ....
}

Предупреждение PVS-Studio: V612 An unconditional 'break' within a loop. msgs.c 1293

Всё бы хорошо, если бы не оператор 'break'. Будет заменён только один пробел. Впрочем, если убрать 'break' это не поможет. Функция strlen() вернёт ноль, и цикл всё равно остановится.

Аналогичные "одноразовые" циклы:

  • V612 An unconditional 'break' within a loop. msgs.c 1313
  • V612 An unconditional 'break' within a loop. api_common.cpp 1407

Разыменовывание нулевого указателя

0244_Trust_but_Verify_ru/image8.png

char **splitLineCSV(....)
{
  ....
  if (retstr[curr_str] == NULL)
  {
    *toks = 0;
    FREE(substitutedstring);
    substitutedstring = NULL;
    freeArrayOfString(retstr, strlen(substitutedstring));
    return NULL;
  }
  ....
}

Предупреждение PVS-Studio: V575 The null pointer is passed into 'strlen' function. Inspect the first argument. splitline.c 107

Странный код. В начале явно обнулили указатель 'substitutedstring'. Затем, отдали его на растерзание в функцию strlen().

Скорее всего, вызов функции freeArrayOfString() должен быть расположен выше, чем вызов FREE().

Это была разминка. Теперь рассмотрим более сложный случай.

inline static void create(void * pvApiCtx, const int position,
  const int rows, const int cols, long long * ptr)
{
  int * dataPtr = 0;
  alloc(pvApiCtx, position, rows, cols, dataPtr);
  for (int i = 0; i < rows * cols; i++)
  {
    dataPtr[i] = static_cast<int>(ptr[i]);
  }
}

V522 Dereferencing of the null pointer 'dataPtr' might take place. scilababstractmemoryallocator.hxx 222

В этой функции хотят выделить память, используя функцию alloc(). Может показаться, что функция возвращает значение по ссылке. Последним аргументом является указатель 'dataPtr'. Кажется, что в него и будет записан указатель на выделенный буфер памяти.

Но это не так. Указатель останется равен нулю. Давайте посмотрим, как объявлена функция alloc():

inline static int *alloc(
  void * pvApiCtx, const int position, const int rows,
  const int cols, int * ptr)

Видите, последний аргумент не является ссылкой. Кстати, вообще не понятно, зачем он нужен. Заглянем внутрь функции alloc():

inline static int *alloc(
  void * pvApiCtx, const int position, const int rows,
  const int cols, int * ptr)
{
  int * _ptr = 0;
  SciErr err = allocMatrixOfInteger32(
    pvApiCtx, position, rows, cols, &_ptr);
  checkError(err);
  return _ptr;
}

Последний аргумент 'ptr' вообще не используется.

В любом случае, код выделения памяти неверен. Код должен быть таким:

inline static void create(void * pvApiCtx, const int position,
  const int rows, const int cols, long long * ptr)
{
  int *dataPtr = alloc(pvApiCtx, position, rows, cols, 0);
  for (int i = 0; i < rows * cols; i++)
  {
    dataPtr[i] = static_cast<int>(ptr[i]);
  }
}

Аналогичные ситуации:

  • scilababstractmemoryallocator.hxx 237
  • scilababstractmemoryallocator.hxx 401

Неправильные сообщения об ошибках

Анализатор PVS-Studio часто находит опечатки в обработчиках ошибок. Этот код редко выполняется, и ошибки подолгу остаются незамеченными. Я думаю, из-за таких ошибок мы часто не можем понять, что же с программой не так. Выданное программой диагностическое сообщение не соответствует действительности.

Пример неправильного формирования сообщения об ошибке:

static SciErr fillCommonSparseMatrixInList(....)
{
  ....
  addErrorMessage(&sciErr, API_ERROR_FILL_SPARSE_IN_LIST,
   _("%s: Unable to create list item #%d in Scilab memory"),
   _iComplex ? "createComplexSparseMatrixInList" :
               "createComplexSparseMatrixInList",
   _iItemPos + 1);
  ....
}

Сообщение PVS-Studio: V583 The '?:' operator, regardless of its conditional expression, always returns one and the same value: "createComplexSparseMatrixInList". api_list.cpp 2398

В независимости от значения переменной '_iComplex', всегда будет распечатано "createComplexSparseMatrixInList".

Аналогично:

  • api_list.cpp 2411
  • api_list.cpp 2418
  • api_list.cpp 2464
  • api_list.cpp 2471

Теперь рассмотрим обработчик ошибки, который никогда не получит управление:

#define __GO_FIGURE__ 9
#define __GO_UIMENU__ 21
int sci_uimenu(char *fname, unsigned long fname_len)
{
  ....
  if (iParentType == __GO_FIGURE__ &&
      iParentType == __GO_UIMENU__)
  {
    Scierror(999, _("%s: Wrong type for input argument #%d: ")
             _("A '%s' or '%s' handle expected.\n"), 
             fname, 1, "Figure", "Uimenu");
    return FALSE;
  }
  ....
}

Предупреждение PVS-Studio: V547 Expression 'iParentType == 9 && iParentType == 21' is always false. Probably the '||' operator should be used here. sci_uimenu.c 99

Условие (iParentType == __GO_FIGURE__ && iParentType == __GO_UIMENU__) никогда не выполняется. Переменная не может быть одновременно равна числу 9 и числу 21. Я думаю, здесь хотели написать так:

if (iParentType != __GO_FIGURE__ &&
    iParentType != __GO_UIMENU__)

Ещё один, особенно сладкий пример.

int set_view_property(....)
{
  BOOL status = FALSE;
  ....
  status = setGraphicObjectProperty(
    pobjUID, __GO_VIEW__, &viewType, jni_int, 1);

  if (status = TRUE)
  {
    return SET_PROPERTY_SUCCEED;
  }
  else
  {
    Scierror(999, _("'%s' property does not exist ")
      _("for this handle.\n"), "view");
    return  SET_PROPERTY_ERROR ;
  }
  ....
}

Предупреждение PVS-Studio: V559 Suspicious assignment inside the condition expression of 'if' operator: status = 1. set_view_property.c 61

Ошибка здесь: "if (status = TRUE)". Вместо сравнения, происходит присваивание.

Отсутствие выбора

0244_Trust_but_Verify_ru/image9.png

Функция, которую можно явно сократить. Видимо, она написана с помощью Copy-Paste, и в скопированном коде что-то забыли поправить.

static int uf_union  (int* uf, int s, int t) {
  if (uf_find(uf,s) < uf_find(uf,t)) 
  {
    uf[uf_find(uf,s)] = uf_find(uf,t); 
    return (uf_find(uf,t)); 
  }
  else
  {
    uf[uf_find(uf,s)] = uf_find(uf,t); 
    return (uf_find(uf,t)); 
  }
}

Предупреждение PVS-Studio: V523 The 'then' statement is equivalent to the 'else' statement. taucs_scilab.c 700

Независимо от условия выполняются идентичные действия.

Теперь другая ситуация. Здесь совпадают условия:

int sci_xset( char *fname, unsigned long fname_len )
{
  ....
  else if ( strcmp(cstk(l1), "mark size") == 0)
  ....
  else if ( strcmp(cstk(l1), "mark") == 0)  
  ....
  else if ( strcmp(cstk(l1), "mark") == 0)
  ....
  else if ( strcmp(cstk(l1), "colormap") == 0)
  ....
}

Предупреждение PVS-Studio: V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 175, 398. sci_xset.c 175

Есть ещё несколько неправильных условий:

  • sci_xset.c 159
  • h5_readdatafromfile_v1.c 1148
  • h5_readdatafromfile.c 1010

Классика

Пожалуй, я выделил самый частый ляп в программах на языке Си/Си++. В начале указатель разыменовывается, а уже затем проверяется на равенство нулю. Это не всегда приводит к ошибке. Но безобразие, оно и есть безобразие.

static void appendData(....)
{
  ....
  sco_data *sco = (sco_data *) * (block->work);
  int maxNumberOfPoints = sco->internal.maxNumberOfPoints;
  int numberOfPoints = sco->internal.numberOfPoints;
  
  if (sco != NULL && numberOfPoints >= maxNumberOfPoints)
  ....
}

Предупреждение PVS-Studio: V595 The 'sco' pointer was utilized before it was verified against nullptr. Check lines: 305, 311. canimxy3d.c 305

В начале, осуществляли доступ к членам, используя указатель 'sco':

int maxNumberOfPoints = sco->internal.maxNumberOfPoints;
int numberOfPoints = sco->internal.numberOfPoints;

А потом вдруг вспомнили, что этот указатель надо проверить:

if (sco != NULL .....

Анализатор выдал ещё 61 одно предупреждение V595. Перечислять их в статье не вижу смысла. Привожу их отдельным списком: scilab-v595.txt.

Ещё одна распространённая ситуация - использование неправильных спецификаторов формата (format specifiers) при работе с функцией sprintf() и аналогичных ей. Почти всё, что нашлось, не интересно. Печатаем беззнаковые значения, как знаковые. Поэтому привожу все эти предупреждения тоже списком: scilab-v576.txt.

Из интересного можно отметить только вот это:

#define FORMAT_SESSION "%s%s%s"
char *getCommentDateSession(BOOL longFormat)
{
  ....
  sprintf(line, FORMAT_SESSION, SESSION_PRAGMA_BEGIN,
          STRING_BEGIN_SESSION, time_str, SESSION_PRAGMA_END);
  ....
}

Предупреждение PVS-Studio: V576 Incorrect format. A different number of actual arguments is expected while calling 'sprintf' function. Expected: 5. Present: 6. getcommentdatesession.c 68

Не будет распечатана строка SESSION_PRAGMA_END.

Осторожно, неопределённое поведение

0244_Trust_but_Verify_ru/image10.png

short ezxml_internal_dtd(ezxml_root_t root, char *s, size_t len)
{
  ....
  while (*(n = ++s + strspn(s, EZXML_WS)) && *n != '>') {
  ....
}

Предупреждение PVS-Studio: V567 Undefined behavior. The 's' variable is modified while being used twice between sequence points. ezxml.c 385

Неизвестно, будет вычислено в начале выражение "++s' или выражение "strspn(s, EZXML_WS)". Соответственно, результат может отличаться на разных компиляторах, платформах и так далее.

Другой, более интересный случай. Здесь неопределённое поведение возникает из-за опечатки.

static char **replaceStrings(....)
{
  ....
  int i = 0;
  ....
  for (i = 0; i < nr; i = i++)
  ....
}

V567 Undefined behavior. The 'i' variable is modified while being used twice between sequence points. csvread.c 620

Беда здесь: i = i++.

По всей видимости, хотел написать так:

for (i = 0; i < nr; i++)

Ещё о строках

char *PLD_strtok(....)
{
  ....
  if ((st->start)&&(st->start != '\0'))
  ....
}

Предупреждение PVS-Studio: V528 It is odd that pointer to 'char' type is compared with the '\0' value. Probably meant: *st->start != '\0'. pldstr.c 303

Хотели проверить, что строка не пустая. Но на самом деле указатель два раза сравнивается с NULL. Правильный код:

if ((st->start)&&(st->start[0] != '\0'))

Аналогичный ляп:

V528 It is odd that pointer to 'char' type is compared with the '\0' value. Probably meant: ** category == '\0'. sci_xcospalload.cpp 57

Следующий код по всей видимости недописан:

int sci_displaytree(char *fname, unsigned long fname_len)
{
  ....
  string szCurLevel = "";
  ....
  //Add node level
  if (szCurLevel != "")
  {
    szCurLevel + ".";
  }
  ....
}

Предупреждение PVS-Studio: V655 The strings was concatenated but are not utilized. Consider inspecting the 'szCurLevel + "."' expression. sci_displaytree.cpp 80

Код, который работает благодаря везению

0244_Trust_but_Verify_ru/image11.png

static int sci_toprint_two_rhs(void* _pvCtx,
                               const char *fname)
{
  ....
  sprintf(lines, "%s%s\n", lines, pStVarOne[i]);
  ....
}

Предупреждение PVS-Studio: V541 It is dangerous to print the string 'lines' into itself. sci_toprint.cpp 314

Функция sprintf() сохраняет результат своей работы в буфер 'lines'. При этом, этот же буфер является одной из входных строк. Так делать не хорошо. Код вполне может работать. Но это опасно. При смене компилятора можно получить неожиданный и неприятный результат.

Аналогичная ситуация: sci_coserror.c 94

Пример кода, который работает, хотя и не верен:

typedef struct JavaVMOption {
    char *optionString;
    void *extraInfo;
} JavaVMOption;

JavaVMOption *options;

BOOL startJVM(char *SCI_PATH)
{
  ....
  fprintf(stderr, "%d: %s\n", j, vm_args.options[j]);
  ....
}

Предупреждение PVS-Studio: V510 The 'fprintf' function is not expected to receive class-type variable as fourth actual argument. jvm.c 247

Здесь хотели распечатать строку, на которую ссылается указатель 'optionString'. Правильный код должен был быть таким:

fprintf(stderr, "%d: %s\n", j, vm_args.options[j].optionString);

Но на самом деле, в качестве аргумента функция fprintf() примет объект типа JavaVMOption. Код работает благодаря чудесному стечению обстоятельств.

Во-первых, член 'optionString' расположен в начале структуры. Поэтому именно его возьмет функция fprintf() и обработает его как указатель на строку.

Во-вторых, после этого функция ничего не распечатывает. Следовательно, не будет распечатан мусор (содержимое переменной 'extraInfo', которая тоже попадёт в стек).

Аллилуйя!

Неработающий цикл

static void reinitdoit(double *told)
{
  int keve = 0, kiwa = 0;
  ....
  kiwa = 0;
  ....
  for (i = 0; i < kiwa; i++)
  ....
}

V621 Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or won't be executed at all. scicos.c 4432

Здесь что-то не так. Переменная 'kiwa' всегда равна нулю. Цикл не выполняется. Возможно, код недописан.

Что не вошло в статью

Если честно, я уже устал просматривать отчёт и писать эту статью. Поэтому я решил остановиться. Пожалуй, можно было упомянуть про ещё пару подозрительных мест. Но я посчитал их несущественными, и лень победила. Плюс наверняка я что-то пропустил, так как не знаком с проектом. Поэтому рекомендую авторам самостоятельно проверить проект, используя анализатор PVS-Studio.

Примечание. Тем, кто решил проверить один раз проверить проект и не покупать анализатор, хочу напомнить, что это совершенно бессмысленное действие. Вся суть статического анализа в регулярных проверках, а не в разовых запусках. Вы допускаете опечатку и анализатор сразу её обнаруживает. Сокращается время на тестирование, отладку и работу с ошибками, появляющимися в bug-трекере. См. также статью: "Лев Толстой и статический анализ кода".

Примечание

Обязательно кто-то спросит, какая версия Scilab проверялась. К сожалению, не самая свежая. Где-то полтора месяца я проверил этот проект, выписал подозрительные фрагменты кода. И... И забыл про этот файл. Было много работы, связанной со сравнением анализаторов. Сейчас я набрёл на этот файл, и долго вспоминал, что это такое было. Я проверяю так много проектов, что у меня в голове уже всё перепуталось, и я даже не помню, смотрел я какой-то проект или нет.

Впрочем, ничего страшного. Сейчас я напишу эту статью. Её увидят авторы Scilab и сами проверят проект. Цель моих статей показать возможности методологии статического анализа, а не найти ошибки в самой последней версии проекта.

Заключение

Используйте статический анализ на регулярной основе. Вы сократите время на устранение глупых ошибок и сможете потратить больше времени на что-то полезное.

Для средних и больших проектов, где требуются ночные проверки, доработка анализатора, интеграция с MSBuild, поддержка Visual Studio 2005/2008 и так далее, мы предлагаем инструмент PVS-Studio.

Дополнительные ссылки

Популярные статьи по теме
Под капотом SAST: как инструменты анализа кода ищут дефекты безопасности

Дата: 26 Янв 2023

Автор: Сергей Васильев

Сегодня речь о том, как SAST-решения ищут дефекты безопасности. Расскажу, как разные подходы к поиску потенциальных уязвимостей дополняют друг друга, зачем нужен каждый из них и как теория ложится на…
Ложные представления программистов о неопределённом поведении

Дата: 17 Янв 2023

Автор: Гость

Неопределённое поведение (UB) – непростая концепция в языках программирования и компиляторах. Я слышал много заблуждений в том, что гарантирует компилятор при наличии UB. Это печально, но неудивитель…
Топ-10 ошибок в C++ проектах за 2022 год

Дата: 29 Дек 2022

Автор: Владислав Столяров

Дело идёт к Новому году, а значит, самое время традиционно вспомнить десять самых интересных срабатываний, которые нашёл PVS-Studio в 2022 году.
PVS-Studio и RPCS3: лучшие предупреждения в один клик

Дата: 12 Дек 2022

Автор: Александр Куренев

Best Warnings — режим анализатора, оставляющий в окне вывода 10 лучших предупреждений. Мы предлагаем вам ознакомиться с обновлённым режимом Best Warnings на примере проверки проекта RPCS3.
Holy C++

Дата: 23 Ноя 2022

Автор: Гость

В этой статье постараюсь затронуть все вещи, которые можно без зазрения совести выкинуть из С++, не потеряв ничего (кроме боли), уменьшить стандарт, нагрузку на создателей компиляторов, студентов, из…

Комментарии (0)

Следующие комментарии next comments
close comment form
Unicorn with delicious cookie
Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо