>
>
>
V2609. MISRA. There should be no occurr…


V2609. MISRA. There should be no occurrence of undefined or critical unspecified behaviour.

Данное диагностическое правило основано на руководстве MISRA (Motor Industry Software Reliability Association) по разработке программного обеспечения.

Это правило актуально только для C. Если в программе возникает неопределённое поведение, то у программиста нет никаких гарантий относительно её исполнения. Такое поведение недопустимо.

Если в программе возникает критическое неуточнённое поведение, это означает, что в зависимости от компилятора и его конфигурации возможно различное исполнение кода. Такое поведение также недопустимо.

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

Однако существует множество ситуаций, которые могут привести к неопределённому или критическому неуточнённому поведению и которые можно распознать алгоритмически. Рассмотрим некоторые из таких случаев.

Часто можно определить потенциальное разыменование нулевого указателя. Рассмотрим следующий фрагмент кода:

void foo()
{
  int len = GetLen();
  char *str = (char *) malloc(mlen + 1);
  str[len] = '\0';
}

При выполнении этого фрагмента кода может произойти разыменование нулевого указателя. Если функция 'malloc' не сможет выделить память, то она запишет 'nullptr' в переменную 'str'. Тогда в выражении 'str[len]' произойдет разыменование 'nullptr', что является неопределенным поведением. Анализатор выдаст следующее предупреждение:

V2609 Undefined behaviour should not occur. There might be dereferencing of a potential null pointer 'str'. Check lines: 4, 5.

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

  • В случае, если переменная 'len' имеет большое значение, выражение 'str[len]' может ссылаться на достаточно удаленные ячейки памяти доступные для записи. Запись туда нуля приведет к непредсказуемым последствиям. Т.е. к тому самому неопределенному поведению.
  • Остановка программы — это тоже критическая ошибка для некоторых приложений.
  • В некоторых микроконтроллерах младшие адреса адресного пространства не защищены от записи, и запись по нулевому указателю никак не будет отловлена операционной системой. Собственно, часто никакой операционной системы и нет.

Более подробно все это рассмотрено в статье "Почему важно проверять, что вернула функция malloc".

Еще одной ситуацией, которую можно распознать алгоритмически, является модификация переменной между двумя точками следования и неоднократное обращение к ней.

Рассмотрим следующий фрагмент кода:

void foo()
{
  int *ptr;
  ....
  *ptr++ = *(ptr + 1);
}

Здесь предполагается, что сначала произойдет увеличение указателя 'ptr' на 1. Затем произойдет вычисление выражения 'ptr + 1', и использоваться будет уже новое значение 'ptr'.

Однако такое предположение программиста неверно. Дело в том, что между двумя точками следования происходит два обращения к переменной 'ptr', одно из которых модифицирует ее значение. В таком случае поведение программы неопределенное.

Анализатор выдаст следующее предупреждение:

V2609 Undefined behaviour should not occur. The 'bufl' variable is modified while being used twice between sequence points.

Также можно определить некорректное использование битовых операторов сдвига. Рассмотрим следующий код:

void foo()
{
  int delta = -2;
  ....
  int expr = DoSomeCalculations();
  expr <<= delta;
}

Здесь переменная 'expr' сдвигается влево на -2 бита. Сдвиг на отрицательное число битов является некорректной операцией и приводит к неопределенному поведению.

Данная диагностика классифицируется как:

  • MISRA-C-1.3