Запутанный анализ потока управления из Parasoft C test

#c #exception #static-analysis #control-flow #parasoft

#c #исключение #статический анализ #control-flow #parasoft

Вопрос:

Мы используем Parasoft C test для статического анализа нашего кода. У него возникли проблемы с кодом, подобным следующему:

 void foo(int* x) {
    try {
        bar();
    } catch(...) {
        delete x;
        throw;
    }

    *x;
}
  

В *x; строке предупреждается, что:

Впоследствии к освобожденной памяти не следует обращаться ни при каких обстоятельствах

Каким-то образом делается вывод, что поток управления может проходить в catch(...) блок, удалять x , проходить мимо throw; и достигать *x; . Я попробовал throw std::exception(""); и пару других и получил то же самое. Parasoft, безусловно, знает об исключениях и включает их в свой поток управления, потому что существует множество других тестов, которые включают проверку исключений. Это просто запутано в данном случае, или действительно есть какой-то способ для выполнения этой программы, чтобы выполнить оба delete x; и *x; ?

Комментарии:

1. Мне кажется, что он запутывается.

2. Я бы сказал, что это запутанно, но вам следует спросить Parasoft. Я бы также сказал, что если у вас есть реальный код, подобный этому, вы делаете что-то неправильно.

3. Можете ли вы извлечь delete x; во встроенный метод? Или Parasoft знает, что нужно заглянуть и туда?

4. Вы могли бы попробовать добавить abort() сразу после throw . Но кто знает, возможно, это будет диагностировано как мертвый код…

5. Это может привести к проверке bar (); Если известно, что bar () освобождает x (или статически известно, что x освобождается перед вводом foo ), это не было бы ошибкой. Во всех остальных случаях анализ потока запутан 🙂

Ответ №1:

Итак, инструмент неверен (я знаю, об этом говорилось ранее), и я предполагаю, что вы не хотите игнорировать предупреждение.

Я согласен с комментарием @Pascal о том, что несколько опасно переписывать код только для того, чтобы обойти ограничения некоторых инструментов. Что вы можете сделать, так это отключить это предупреждение только для файлов, в которых у вас в данный момент возникает эта проблема.

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

Для нового кода вам придется принять некоторый стиль, понятный инструменту. Это не такая проблема, потому что это будет код, над которым вы работаете в данный момент, поэтому было бы меньше проблем, если бы вам нужно было немного переписать его, чтобы избавиться от предупреждений.

Несмотря на правильность, существующий стиль далек от идеала.

Я бы рекомендовал хранить указатели, подобные x , в auto_ptr. Это автоматически удалит содержимое auto_ptr, если оно выходит за рамки — если вы явно не удалите его из auto_ptr. Это намного проще для глаз, а также прекрасно документирует, что эта функция становится владельцем указателя.

 void foo(auto_ptr<int> x)
{
    bar();
    *x;
}
  

Я ожидаю, что у ParaSoft не возникнет проблем с этим кодом.

Ответ №2:

Возможно, это глупое предложение, но что скажет Parasoft, если вы оставите catch на конец? Т.е.

 void foo(int* x) 
  {
  try 
    {
    bar();
    *x;
    } 
  catch(...) 
    {
    delete x;
    throw;
    }      
  }
  

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

Комментарии:

1. Если это помогает обойти проблему, это не совсем к делу — хотя я склоняюсь к тому, чтобы просто игнорировать явно неправильный вывод с помощью инструмента анализа Parasoft.

2. Я поддерживаю комментарий. Если вы начнете обходить ложные срабатывания путем изменения кода, вы приведете к появлению ошибок. Это может быть один раз из 50 или только один раз из 200. Это не имеет значения: вы тратите время на внесение ошибок.

Ответ №3:

Быстрое обновление:

1) Вышеупомянутое правило вообще не является правилом анализа потока. При этом правило (ИДЕНТИФИКАТОР MRM-31) было улучшено в C test 9.2.0 и более поздних версиях. Для этого также есть соответствующее правило анализа потока в C test (ID: BD-RES-FREE), которое должно делать то, что вы хотите.