Вебинар: ГОСТ Р 71207–2024 — Статический анализ программного обеспечения. Процессы - 13.09
Статические анализаторы кода могут учитывать данные, которые передаются между процедурами/функциями, чтобы выявить больше ошибок и потенциальных уязвимостей. Это важно при выявлении таких ошибок, как утечки памяти, разыменование нулевых указателей, выход за границы массивов и т.д. Это связано с тем, что создание некого ресурса, его использование и освобождение часто разнесены по разным функциях.
Для выявления ошибок, возникающих при взаимодействии нескольких процедур, статические анализаторы используют технологию межпроцедурного контекстно-чувствительного анализа кода.
Поясним это на примере реальной ошибки, найденной с помощью анализатора PVS-Studio в коде проекта AvalonStudio (C#).
Рассмотрим в начале функцию IsBuiltInType. Обратите внимание, что если её входной аргумент cursor окажется нулевой ссылкой, то функция вернёт значение false.
private static bool IsBuiltInType(ClangType cursor)
{
var result = false;
if (cursor != null && ....)
{
return true;
}
return result;
}
Теперь рассмотрим другой фрагмент кода, где вызывается рассмотренная функция:
private static StyledText InfoTextFromCursor(ClangCursor cursor)
{
....
if (cursor.ResultType != null)
{
result.Append(cursor.ResultType.Spelling + " ",
IsBuiltInType(cursor.ResultType) ? theme.Keyword
: theme.Type);
}
else if (cursor.CursorType != null)
{
switch (kind)
{
....
}
result.Append(cursor.CursorType.Spelling + " ",
IsBuiltInType(cursor.ResultType) ? theme.Keyword
: theme.Type);
}
....
}
Если cursor.ResultType != null, то выполняется тело первого оператора if. Соответственно, если управление будет предано внутрь тела второго оператора if, то точно известно, что ссылка cursor.ResultType является нулевой.
Изучим место вызова рассмотренной ранее функции IsBuiltInType:
result.Append(cursor.CursorType.Spelling + " ",
IsBuiltInType(cursor.ResultType) ? theme.Keyword
: theme.Type);
Анализатор знает, что cursor.ResultType — нулевая ссылка. Из этого он делает вывод, что при таком входном аргументе функция всегда возвращает false.
Это и есть межпроцедурный контекстно-чувствительный анализ.
Если условие тернарного оператора всегда ложно, то это подозрительно, о чём анализатор и сообщает, выдавая предупреждение:
V3022 Expression 'IsBuiltInType(cursor.ResultType)' is always false.
Действительно, если присмотреться к коду, то можно заметить опечатку. В теле второго оператора if при вызове функции IsBuiltInType в качестве фактического аргумента следует передать переменную cursor.CursorType.
Исправленный вариант кода:
private static StyledText InfoTextFromCursor(ClangCursor cursor)
{
....
if (cursor.ResultType != null)
{
result.Append(cursor.ResultType.Spelling + " ",
IsBuiltInType(cursor.ResultType) ? theme.Keyword
: theme.Type);
}
else if (cursor.CursorType != null)
{
switch (kind)
{
....
}
result.Append(cursor.CursorType.Spelling + " ",
IsBuiltInType(cursor.CursorType) ? theme.Keyword
: theme.Type);
}
....
}
ГОСТ Р 71207-2024 — Статический анализ программного обеспечения. В разделе терминов дано следующее определение межпроцедурного контекстно-чувствительного анализа:
Статический анализ, при котором выявляемые свойства программы учитывают взаимодействие нескольких процедур, в том числе — возникающее в результате выполнения нескольких процедур или вызовов процедурами друг друга, а также контексты их вызова.
Примечание. В ходе анализа учитывается контекст вызова процедуры при обработке её вызова, т.е. сопоставляется информация из вызывающей и вызываемой процедур применительно к каждому месту вызова и его окружению: фактическим параметрам, состоянию глобальных переменных и т.п.
Ситуация усложняется, если требуется анализировать взаимодействие функций, находящихся в различных единицах трансляции/программных модулях. В этом случае говорят, что выполняется межмодульный анализ.
Дополнительные ссылки
0