Как-то так несправедливо, что в наших заметках мы почти не уделяем внимания совершенствованию внутренних механизмов анализатора, в отличие от новой диагностики.
Итак, для разнообразия, давайте взглянем на новое полезное усовершенствование в анализе потоков данных.
Все началось с твита от JetBrains CLion IDE
На днях я увидел в Твиттере пост от JetBrains о новых возможностях статического анализатора, встроенного в CLion.Поскольку в ближайшее время мы планируем выпустить плагин PVS-Studio для CLion, я не мог пройти мимо и не написать, что мы тоже не слепы.
И что есть смысл попробовать PVS-Studio в качестве плагина для CLion, чтобы найти еще больше ошибок.
Ну, я с ними переписывался чуть поприятнее:
Но они великолепны! Улучшенный анализ потока данных и рассказать миру.
Чем мы хуже? Ведь внутри анализатора мы тоже постоянно что-то совершенствуем, в том числе и тот самый механизм анализа потока данных.
И сейчас я пишу эту заметку.
Что интересного в Data Flow?
Пару дней назад было сделано обновление для клиента, который описал ошибку, которую, к сожалению, анализатор PVS-Studio не смог заранее обнаружить в его коде.Анализатор в некоторых случаях путался со значениями беззнаковых переменных, если происходило переполнение.
Проблема была в примерно таком коде:
Анализатор не смог понять, что переменная останавливаться всегда присваивается значение ЛОЖЬ .bool foo() { unsigned N = 2; for (unsigned i = 0; i < N; ++i) { bool stop = (i - 1 == N); if (stop) return true; } return false; }
Почему ЛОЖЬ ? Давайте быстро посчитаем:
- диапазон значений переменной я = [0; 1] ;
- возможные значения выражения я-1 = [0; 0] U [UINT_MAX; UINT_MAX] ;
- значение переменной N, равное двум, не входит в набор {0, UINT_MAX};
- условие всегда ложно.
Здесь нет неопределенного поведения, поскольку при работе с беззнаковым типом происходит переполнение переноса.
Теперь мы научили PVS-Studio корректно работать с такими выражениями и выдавать соответствующие предупреждения.
Интересно, что это изменение привело к каскаду других улучшений.
Например, были ложные срабатывания, связанные с обработкой длины строк.
Борьба с ними привела к новым улучшениям и обучению анализатора лучше понимать, как и почему функционируют такие функции, как стрлен .
Сейчас мы на практике покажем, о каких улучшениях идет речь.
В тестовой базе открытых проектов, на которых мы регулярно проводим регрессионное тестирование ядра анализатора, есть эмулятор FCEUX .
После внесения доработок мы смогли обнаружить в коде интересную ошибку функции Assemble. int Assemble(unsigned char *output, int addr, char *str) {
output[0] = output[1] = output[2] = 0;
char astr[128],ins[4];
if ((!strlen(str)) || (strlen(str) > 0x127)) return 1;
strcpy(astr,str);
.
}
Вы видите ошибку? Честно говоря, мы не сразу это заметили и подумали, что что-то сломали.
И когда мы поняли, в чем дело, мы еще раз восхитились тем, насколько может быть полезен статический анализ.
Предупреждение PVS-Studio: V512 Вызов функции strcpy приведет к переполнению буфера astr. asm.cpp 21 Все еще не видите ошибку? Давайте внимательно посмотрим на код. Для начала удалим все неактуальное: int Assemble(char *str) {
char astr[128];
if ((!strlen(str)) || (strlen(str) > 0x127)) return 1;
strcpy(astr,str);
.
}
Имеется локальный массив размером 128 байт, в который планируется скопировать строку, переданную в качестве аргумента.
Копирование не следует производить, если строка пуста или содержит более 127 символов (не считая терминального нуля).
Пока все логично и правильно? На первый взгляд да.
Но что это?! Что это за константа? 0x127 ?! Это вовсе не 127. Совсем не 127 :) Константа указывается в шестнадцатеричном формате.
Если перевести его в десятичную форму, то получится 295. Таким образом, написанный код эквивалентен следующему: int Assemble(char *str) {
char astr[128];
if ((!strlen(str)) || (strlen(str) > 295)) return 1;
strcpy(astr,str);
.
}
Как видите, проверка никак не защищает от переполнения буфера, и анализатор корректно предупреждает о проблеме.
Раньше анализатор не мог найти ошибку, не понимая, что две функции стрлен работайте с одной строкой.
И эта строка не меняется между двумя вызовами стрлен .
С точки зрения программиста все это очевидно, но анализатор нужно научить во всем этом разбираться :).
Теперь PVS-Studio из выражения выводит, что длина строки ул.
лежит в диапазоне [1.295], а это значит, что массив может выйти за границы, если попытаться скопировать его в буфер астр .
Новые испытания
Описанная ошибка присутствует и в текущей версии кодовой базы проекта FCEUX. Но мы его не найдем, так как код изменился и теперь длина строки хранится в переменной.Это нарушает связь между строкой и ее длиной.
К сожалению, о новой версии кода анализатор пока молчит: int Assemble(unsigned char *output, int addr, char *str) {
output[0] = output[1] = output[2] = 0;
char astr[128],ins[4];
int len = strlen(str);
if ((!len) || (len > 0x127)) return 1;
strcpy(astr,str);
.
}
Человеку этот код может показаться еще более простым, но с точки зрения статического анализа сложно уследить за значениями.
Необходимо учитывать, что значение переменной Лен длина строки ул.
.
Кроме того, вам необходимо внимательно следить за тем, когда эта связь нарушается при изменении содержимого строки или переменной.
Лен .
Анализатор PVS-Studio пока этого сделать не может. Но вы же видите, куда нам можно и нужно развиваться! Со временем мы научимся находить ошибки в этом новом коде.
Кстати, читатель может задаться вопросом, почему мы анализируем старый код проекта и не обновляем его регулярно? Это просто.
Если мы обновим проекты, мы не сможем проводить регрессионное тестирование.
Не будет ясно, произошли ли изменения в работе анализатора из-за изменений в коде самого анализатора или из-за изменений в коде проверяемого проекта.
Поэтому мы не обновляем проекты с открытым исходным кодом, используемые в качестве основы для тестирования.
А чтобы протестировать анализатор на современном коде, написанном на C++14, C++17 и т.д., мы постепенно пополняем базу новыми проектами.
Например, сравнительно недавно мы добавлен коллекция библиотек C++ только для заголовков ( потрясающе-hpp ).
Заключение
Разработка механизмов анализа потоков данных интересна и полезна.А если вам интересно узнать больше о работе статических анализаторов кода в целом, то предлагаем вашему вниманию следующие наши публикации:
- Анализатор кода неправильный, да здравствует анализатор
- Ложные срабатывания в PVS-Studio: насколько глубока кроличья нора
- Технологии, используемые в анализаторе кода PVS-Studio для поиска ошибок и потенциальных уязвимостей
- Использование машинного обучения в статическом анализе исходного кода программ
Если вы хотите поделиться этой статьей с англоязычной аудиторией, воспользуйтесь ссылкой для перевода: Андрей Карпов.
PVS-Studio узнает, что такое strlen .
Теги: #информационная безопасность #C++ #c #статический анализ кода #pvs-studio #статический анализатор кода #strlen #fceux
-
Веб-Хостинг Whm Для Администраторов Серверов
19 Oct, 24 -
Заблуждение О Рынке Даркнета Теперь Раскрыто
19 Oct, 24 -
Виды Текстов
19 Oct, 24 -
Мой Побег(Html)
19 Oct, 24 -
«Комстар» Купит По-Крупному
19 Oct, 24