2. ООО "СиПроВер" (www.viva64.com)
• Занимаемся разработкой, продвижением и продажей
собственного программного продукта.
• Офис: г. Тула, 200 км от Москвы.
• Штат: 24 человека
3. PVS-Studio
• Более 320 диагностик для C, C++
• Более 120 диагностик для C#
• Windows
• Linux
• Плагин для Visual Studio
• Быстрый старт (мониторинг компиляции)
• SonarQube
4. Наши достижения
• Для рекламы мы проверяем открытые проекты. На данный момент мы
проверили около 270 проектов.
• Побочный эффект: нашли более 10000 ошибок в открытых проектах, не задаваясь
такой целью.
• В среднем 40 ошибок на проект – вроде немного.
• Важно ещё раз подчеркнуть, что это побочный эффект. У нас нет цели найти как
можно больше ошибок. Часто мы останавливаемся, когда нашли достаточное
количество дефектов в проекте, чтобы написать статью.
• Вывод: легко проверять даже незнакомые проекты и находить в них ошибки.
6. Мы не используем математический
аппарат грамматик
• Анализатор работает на более высоком уровне
• Анализируется дерево разбора
• Для построения дерева мы опираемся на уже существующие
компоненты:
• Внешний препроцессор
• Библиотека OpenC++, которую мы дорабатываем с развитием C++ по
мере необходимости (собственно от OpenC++ уже ничего не осталось)
• При работе с C# кодом мы опираемся на Roslyn
7. Мы не используем методики доказательства
правильности программ (program proof).
• PVS-Studio не имеет ничего общего с Prototype Verification System
(PVS) http://pvs.csl.sri.com/
• PVS-Studio сокращение от OOO "Program Verification Systems"
(ООО "Системы программной верификации")
8. Мы не используем поиск подстрок (string matching)
и регулярные выражения (regular expressions)
• Тупиковый путь
• Подводит даже в простейших ситуациях
• Пример: if (A+B == A+B)
• A+B == B+A
• A+(B) == (A)+B
• ((A+B)) == A+B
• Более фатально: типы, размеры объектов, наследование, значения
переменных и так далее
9. Что МЫ используем
Детали анализа C++ и C# кода разнятся, но мы не будем делать в
докладе уточнения.
10. Pattern-based analysis
• Сопоставление с шаблоном на основе дерева разбора
• Применяется для поиска мест в исходном коде, которые похожи
на известные шаблоны кода с ошибкой
• Сложность диагностик крайне разнится
• В некоторых случаях работают эмпирические алгоритмы
11. if ((*path)[0]->e->dest->loop_father != path->last()->e->....)
{
delete_jump_thread_path (path);
e->aux = NULL;
ei_next (&ei;);
}
else
{
delete_jump_thread_path (path);
e->aux = NULL;
ei_next (&ei;);
}
Простой случай: copy-paste
Проект GCC
V523 The 'then' statement is equivalent to the 'else' statement. tree-ssa-
threadupdate.c 2596
12. Средний случай: проверка не той переменной
public override Predicate JoinWith(Predicate other)
{
var right = other as PredicateNullness;
if (other != null)
{
if (this.value == right.value)
{
Проект CodeContracts
V3019 Possibly an incorrect variable is compared to null after type conversion
using 'as' keyword. Check variables 'other', 'right'. CallerInvariant.cs 189
13. Сложный случай: неудачный макрос
#define ICB2400_VPINFO_PORT_OFF(chan)
(ICB2400_VPINFO_OFF +
sizeof (isp_icb_2400_vpinfo_t) +
(chan * ICB2400_VPOPT_WRITE_SIZE))
off += ICB2400_VPINFO_PORT_OFF(chan - 1);
V733 It is possible that macro expansion resulted in incorrect evaluation
order. Check expression: chan - 1 * 20. isp.c 2301
Проект FreeBSD
14. Type inference
• Вывод типов на основе семантической модели программы
позволяет анализатору иметь полную информацию о всех
переменных и выражениях, встречающихся в коде
• Важно для выявления ошибок
• Важно для исключений
• Особенно важна информация о классах
15. Типы важны для выявления ошибок
Проект Cocos2d-x
WCHAR *gai_strerrorW(int ecode);
#define gai_strerror gai_strerrorW
fprintf(stderr, "net_listen error for %s: %s",
serv, gai_strerror(n));
V576 Incorrect format. Consider checking the fourth actual argument of
the 'fprintf' function. The pointer to string of char type symbols is
expected. ccconsole.cpp 341
16. Типы важны для исключений
// volatile переменная присваивается сама себе
volatile int *ptr;
....
*ptr = *ptr; // Нет срабатывания V570
17. Особенно важна информация о классах:
например, иерархия наследования
class sg_throwable : public std::exception { .... };
class sg_exception : public sg_throwable { .... };
if (!aInstall) {
sg_exception("missing argument to scheduleToUpdate");
}
V596 The object was created but it is not being used. The 'throw' keyword
could be missing: throw sg_exception(FOO); root.cxx 239
Проект FlightGear
18. Symbolic execution
• Символьное выполнение позволяет вычислять значения
переменных, которые могут приводить к ошибкам, производить
проверку диапазонов (range checking) значений
• Один из самых важных механизмов:
• Переполнения
• Утечки памяти
• Выход за границы массива
• Нулевые указатели / ссылки
• Бессмысленные условия
• Деление на 0
• И так далее
19. Значения переменных: размер массива,
индексы
Handle<YieldTermStructure> md0Yts() {
double q6mh[] = {
0.0001,0.0001,0.0001,0.0003,0.00055,0.0009,0.0014,0.0019,
0.0025,0.0031,0.00325,0.00313,0.0031,0.00307,0.00309,
........................................................
0.02336,0.02407,0.0245 }; 60 элементов
....
for(int i=0;i<10+18+37;i++) { i < 65
q6m.push_back(
boost::shared_ptr<Quote>(new SimpleQuote(q6mh[i])));
Проект QuantLib
V557 Array overrun is possible. The value of 'i' index could reach 64.
markovfunctional.cpp 176
20. Значения переменных: использование
условий для определения диапазона
std::string rangeTypeLabel(int idx)
{
const char* rangeTypeLabels[] = {"Self", "Touch", "Target"};
if (idx >= 0 && idx <= 3)
return rangeTypeLabels[idx];
else
return "Invalid";
}
V557 Array overrun is possible. The value of 'idx' index could reach 3.
esmtool labels.cpp 502
Проект OpenMW
21. Значения функций
static inline size_t UnboxedTypeSize(JSValueType type)
{
switch (type) {
.......
default: return 0;
}
}
Minstruction *loadUnboxedProperty(size_t offset, ....)
{
size_t index = offset / UnboxedTypeSize(unboxedType);
Проект Thunderbird
V609 Divide by zero. Denominator range [0..8]. ionbuilder.cpp 10922
22. Значения переменных: указатели / ссылки
if (providerName == null)
{
ProviderNotFoundException e =
new ProviderNotFoundException(
providerName.ToString(),
SessionStateCategory.CmdletProvider,
"ProviderNotFound",
SessionStateStrings.ProviderNotFound);
throw e;
V3080 Possible null dereference. Consider inspecting 'providerName'.
System.Management.Automation SessionStateProviderAPIs.cs 1004
Проект PowerShell
23. Method annotations
• Аннотирование методов предоставляет больше информации об
используемых методах, чем может быть получено путём анализа
только их сигнатуры.
• C/C++. На данный момент проаннотировано 6570 функций
(стандартные библиотеки C и C++, POSIX, MFC, Qt, ZLib и так
далее).
• C#. На данный момент проаннотировано 920 функций.
24. Пример аннотирования функции memcmp
C_"int memcmp(const void *buf1, const void *buf2, size_t count);"
ADD(REENTERABLE | RET_USE | F_MEMCMP | STRCMP | HARD_TEST | INT_STATUS,
nullptr, nullptr, "memcmp", POINTER_1, POINTER_2, BYTE_COUNT);
• C_ - вспомогательный механизм контроля аннотаций (юнит-тесты)
• REENTERABLE – повторный вызов с теми же аргументами даст тот-же результат
• RET_USE – результат должен быть использован
• F_MEMCMP – запуск определённых проверок выхода за границы буфера
• STR_CMP – При равенстве функция возвращает 0.
• HARD_TEST – особая функция. Некоторые программисты определяют
собственные такие функции в своих namespace. Не учитывать namespace.
• INT_STATUS – результат явно сравнивать с 1 или -1.
• POINTER_1, POINTER_2 – указатели должны быть не нулевыми и разными.
• BYTE_COUNT – параметр задает количество байт и должен быть больше 0.
25. Аннотация memcmp: проверка результата
bool operator()(const GUID& _Key1, const GUID& _Key2) const
{
return memcmp(&_Key1, &_Key2, sizeof(GUID)) == -1;
}
Проект CoreCLR
V698 Expression 'memcmp(....) == -1' is incorrect. This function can
return not only the value '-1', but any negative value. Consider using
'memcmp(....) < 0' instead. sos util.cpp 142
26. Аннотация memcmp: хранение результата
Проект Firebird
V642 Saving the 'memcmp' function result inside the 'short' type variable is
inappropriate. The significant bits could be lost breaking the program's logic.
texttype.cpp 3
SSHORT TextType::compare(ULONG len1, const UCHAR* str1,
ULONG len2, const UCHAR* str2)
{
....
SSHORT cmp = memcmp(str1, str2, MIN(len1, len2));
if (cmp == 0)
cmp = (len1 < len2 ? -1 : (len1 > len2 ? 1 : 0));
return cmp;
}
27. Аннотация memcmp: неверный аргумент
Проект GLG3D
V575 The 'memcmp' function processes '0' elements. Inspect
the 'third' argument. graphics3D matrix4.cpp 269
bool Matrix4::operator==(const Matrix4& other) const {
if (memcmp(this, &other, sizeof(Matrix4) == 0)) {
return true;
}
...
}
28. static int
psymbol_compare (const void *addr1, const void *addr2,
int length)
{
struct partial_symbol *sym1 = (struct partial_symbol *) addr1;
struct partial_symbol *sym2 = (struct partial_symbol *) addr2;
return (memcmp (&sym1->ginfo.value, &sym1->ginfo.value,
sizeof (sym1->ginfo.value)) == 0
&& .......
Аннотация memcmp: разные аргументы
Проект GDB
V549 The first argument of 'memcmp' function is equal to the second
argument. psymtab.c 1580
30. Аннотация memcmp: нет состояния
Проект PHP
V501 There are identical sub-expressions '!memcmp("auto", charset_hint,
4)' to the left and to the right of the '||' operator. html.c 396
if ((len == 4) /* sizeof (none|auto|pass) */ &&
(!memcmp("pass", charset_hint, 4) ||
!memcmp("auto", charset_hint, 4) ||
!memcmp("auto", charset_hint, 4)))
31. Аннотирование пользовательских функций
• Практически отсутствует (кроме отдельных элементов, как
собственной функции printf)
• Развивать механизм нет смысла
• В больших проектах никто не станет тратить месяцы на разметку
• Анализатор должен работать сразу
32. Тестирование анализатора
• Тестирование анализатора важнейшая часть процесса его
разработки
• Самое сложное в статическом анализе: не ругаться
• Большая тестовая база:
• C++ Windows (Visual C++): 120 проектов
• C++ Linux (GCC): ещё 34 проекта
• C# Windows: 54 проекта
33. Вышлем по почте более подробный
вариант презентации
• Написать письмо: support@viva64.com
• Подписаться на твиттер: @Code_Analysis
• Скачать PVS-Studio для Windows:
http://www.viva64.com/ru/pvs-studio/
• Скачать PVS-Studio для Linux:
http://www.viva64.com/ru/pvs-studio-download-linux/