Уязвимость в терминах компьютерной безопасности — недостаток в системе, позволяющий намеренно нарушить её целостность или вызвать неправильную работу. Как показывает практика, даже, казалось бы, незначительный баг может являться серьёзной уязвимостью. Уязвимостей можно избежать, используя различные методики валидации и верификации программного обеспечения, в том числе — статический анализ. О том, как с задачей поиска уязвимостей справляется PVS-Studio, и пойдёт речь.
PVS-Studio — инструмент, предотвращающий не только ошибки, но и уязвимости
Как мы пришли к такому заключению, я расскажу немного ниже. Здесь же я хотел рассказать о том, что представляет из себя PVS-Studio.
PVS-Studio — статический анализатор кода, который ищет ошибки (и уязвимости, в чем вы убедитесь далее) в программах, написанных на C, C++, C#. Работает под Windows и Linux, умеет встраиваться в IDE Visual Studio в качестве плагина. На данный момент в анализаторе реализовано свыше 450 диагностических правил, каждое из которых сопровождается документацией.
На момент написания статьи с помощью PVS-Studio проверено более 280 open source проектов, в которых было найдено свыше 11 000 ошибок. Интересно, сколько из этих ошибок являются уязвимостями…
Загрузить и попробовать PVS-Studio можно на официальном сайте.
Кстати, мы предлагаем лицензии на PVS-Studio экспертам безопасности. Если вы являетесь публичным экспертом в области безопасности и занимаетесь поиском уязвимостей, напишите нам для получения лицензии. Более подробно про это предложение можно почитать в статье "Предоставляем анализатор PVS-Studio экспертам безопасности".
Терминология
Если вы хорошо ориентируетесь в терминологии, знаете, чем CVE отличается от CWE, и что между ними общего, то можете пропустить этот раздел. Остальным советую ознакомиться, чтобы было легче разобраться в дальнейшем.
CWE (Common Weakness Enumeration) — общий перечень дефектов безопасности. Предназначен для разработчиков и специалистов по обеспечению безопасности ПО. Он представляет собой официальный реестр или словарь общих дефектов безопасности, которые могут проявиться в архитектуре, проектировании, коде или реализации ПО, и могут быть использованы злоумышленниками для получения несанкционированного доступа к системе. Данный перечень был разработан в качестве универсального формального языка для описания дефектов безопасности ПО, а также в качестве стандарта для измерения эффективности инструментов, выявляющих такие дефекты, и для распознавания, устранения и предотвращения этих дефектов.
CVE (Common Vulnerabilities and Exposures) — ошибки программы, которые могут быть непосредственно использованы злоумышленниками.
Корпорация MITRE начала работу по классификации уязвимостей ПО в 1999 году, когда был основан перечень общих уязвимостей и подверженностей воздействиям ПО (CVE). В 2005 году в рамках дальнейшего развития системы CVE команда авторов начала работу по предварительной классификации уязвимостей, атак, сбоев и прочих видов проблем безопасности с целью дать определение общим дефектам безопасности ПО. Однако, несмотря на самодостаточность созданной классификации в рамках CVE, она оказалась слишком грубой для определения и классификации методов оценки безопасности кода, используемых анализаторами. Для решения этой проблемы и был создан перечень CWE.
PVS-Studio: иное позиционирование
Предыстория
Мы всегда позиционировали PVS-Studio как инструмент для поиска ошибок в программном коде. В статьях о проверке проектов употребляли соответствующую терминологию: баг, ошибка, опечатка и т.п. Понятно, что разные ошибки имеют разную критичность: вот этот код, скорее всего, просто избыточен и запутывает программиста, а вот из-за этой ошибки вся система падает в 5 утра каждый 3 день. Это было понятно, эта концепция долго не получала дальнейшего развития — ошибки, они и есть ошибки.
Однако со временем оказалось, что некоторые из ошибок, обнаруживаемых PVS-Studio, могут быть трактованы более серьезным образом. Например, неправильное использование функции printf может повлечь за собой куда более негативные последствия, нежели вывод неправильного сообщения в stdout. Когда стало понятно, что с помощью многих диагностических правил можно обнаруживать не просто ошибки, а потенциальные уязвимости (CWE), было принято решение подойти к вопросу более детально и посмотреть, как соотносятся диагностические правила PVS-Studio и CWE.
Отношение предупреждений PVS-Studio и CWE
По результатам работы по выявлению взаимосвязей между предупреждениями PVS-Studio и CWE была составлена следующая таблица:
CWE | PVS-Studio | CWE Description |
---|---|---|
CWE-14 | V597 | Compiler Removal of Code to Clear Buffers |
CWE-36 | V631, V3039 | Absolute Path Traversal |
CWE-121 | V755 | Stack-based Buffer Overflow |
CWE-122 | V755 | Heap-based Buffer Overflow |
CWE-123 | V575 | Write-what-where Condition |
CWE-129 | V557, V781, V3106 | Improper Validation of Array Index |
CWE-190 | V636 | Integer Overflow or Wraparound |
CWE-193 | V645 | Off-by-one Error |
CWE-252 | V522, V575 | Unchecked Return Value |
CWE-253 | V544, V545, V676, V716, V721, V724 | Incorrect Check of Function Return Value |
CWE-390 | V565 | Detection of Error Condition Without Action |
CWE-476 | V522, V595, V664, V757, V769, V3019, V3042, V3080, V3095, V3105, V3125 | NULL Pointer Dereference |
CWE-481 | V559, V3055 | Assigning instead of comparing |
CWE-482 | V607 | Comparing instead of Assigning |
CWE-587 | V566 | Assignment of a Fixed Address to a Pointer |
CWE-369 | V609, V3064 | Divide By Zero |
CWE-416 | V723, V774 | Use after free |
CWE-467 | V511, V512, V568 | Use of sizeof() on a Pointer Type |
CWE-805 | V512, V594, V3106 | Buffer Access with Incorrect Length Value |
CWE-806 | V512 | Buffer Access Using Size of Source Buffer |
CWE-483 | V640, V3043 | Incorrect Block Delimitation |
CWE-134 | V576, V618, V3025 | Use of Externally-Controlled Format String |
CWE-135 | V518, V635 | Incorrect Calculation of Multi-Byte String Length |
CWE-462 | V766, V3058 | Duplicate Key in Associative List (Alist) |
CWE-401 | V701, V773 | Improper Release of Memory Before Removing Last Reference ('Memory Leak') |
CWE-468 | V613, V620, V643 | Incorrect Pointer Scaling |
CWE-588 | V641 | Attempt to Access Child of a Non-structure Pointer |
CWE-843 | V641 | Access of Resource Using Incompatible Type ('Type Confusion') |
CWE-131 | V512, V514, V531, V568, V620, V627, V635, V641, V645, V651, V687, V706, V727 | Incorrect Calculation of Buffer Size |
CWE-195 | V569 | Signed to Unsigned Conversion Error |
CWE-197 | V642 | Numeric Truncation Error |
CWE-762 | V611, V780 | Mismatched Memory Management Routines |
CWE-478 | V577, V719, V622, V3002 | Missing Default Case in Switch Statement |
CWE-415 | V586 | Double Free |
CWE-188 | V557, V3106 | Reliance on Data/Memory Layout |
CWE-562 | V558 | Return of Stack Variable Address |
CWE-690 | V522, V3080 | Unchecked Return Value to NULL Pointer Dereference |
CWE-457 | V573, V614, V730, V670, V3070, V3128 | Use of Uninitialized Variable |
CWE-404 | V611, V773 | Improper Resource Shutdown or Release |
CWE-563 | V519, V603, V751, V763, V3061, V3065, V3077, V3117 | Assignment to Variable without Use ('Unused Variable') |
CWE-561 | V551, V695, V734, V776, V779, V3021 | Dead Code |
CWE-570 | V501, V547, V517, V560, V625, V654, V3022, V3063 | Expression is Always False |
CWE-571 | V501, V547, V560, V617, V654, V694, V768, V3022, V3063 | Expression is Always True |
CWE-670 | V696 | Always-Incorrect Control Flow Implementation |
CWE-674 | V3110 | Uncontrolled Recursion |
CWE-681 | V601 | Incorrect Conversion between Numeric Types |
CWE-688 | V549 | Function Call With Incorrect Variable or Reference as Argument |
CWE-697 | V556, V668 | Insufficient Comparison |
Таблица N1 — Черновой вариант таблицы соответствий CWE и диагностик PVS-Studio
Это не окончательный вариант таблицы, но он даёт некоторое представление о том, как соотносятся между собой предупреждения PVS-Studio и CWE. Теперь стало понятно, что PVS-Studio успешно находит (и всегда находил) в коде программ не просто баги, а потенциальные уязвимости, то есть CWE. На эту тему было написано несколько статей, их перечень приведён в конце текущей статьи.
Базы CVE
Потенциальная уязвимость (CWE) — ещё не есть уязвимость (CVE). Реальные уязвимости, найденные как в open source, так и в проприетарных проектах, собраны на сайте http://cve.mitre.org. Там можно посмотреть описание конкретной уязвимости, найти дополнительные ссылки (например, на обсуждение, бюллетень исправлений уязвимостей, ссылки на коммиты, закрывающие уязвимости, и т.п.). При желании эту базу можно выгрузить целиком в интересующем формате. На момент написания статьи база в текстовом формате представляла собой .txt файл размером порядка 100Мб и объёмом более 2.7 млн строк. Весьма внушительно, согласитесь.
В ходе работы я нашёл ещё один интересный ресурс, который может быть полезен заинтересованным — http://www.cvedetails.com/. Он удобен тем, что даёт такие возможности, как:
- поиск CVE по идентификатору CWE;
- поиск CVE в определённом продукте;
- просмотр статистики появления/исправления уязвимостей;
- просмотр различных таблиц данных, так или иначе связанных с CVE (например, рейтинг фирм, в программных продуктах которых было найдено наибольшее количество уязвимостей);
- и т.п.
Некоторые CVE, которые могли бы быть найдены с помощью PVS-Studio
Я пишу эту статью для того, чтобы продемонстрировать, что анализатор PVS-Studio может защитить приложение от уязвимостей (по крайней мере, от некоторых из них).
Мы никогда не исследовали, можно ли тот или иной дефект, обнаруженный с помощью PVS-Studio, эксплуатировать как уязвимость. Это сложно, да и такой задачи перед нами не стоит. Поэтому я поступлю иным образом: возьму несколько уязвимостей, которые уже были обнаружены и описаны, и продемонстрирую, что их можно было избежать, если бы код регулярно проверялся с помощью PVS-Studio.
Примечание. Описанные в статье уязвимости были найдены не в синтетических примерах, а в реальных исходных файлах, взятых из старых ревизий проектов.
illumos-gate
Первая уязвимость, о которой пойдёт речь, была обнаружена в исходном коде проекта illumos-gate. illumos-gate — проект с открытым исходным кодом (доступным в репозитории на GitHub), формирующий ядро операционной системы, уходящей корнями к Unix и BSD.
Уязвимость имеет кодовое название CVE-2014-9491.
Описание CVE-2014-9491: The devzvol_readdir function in illumos does not check the return value of a strchr call, which allows remote attackers to cause a denial of service (NULL pointer dereference and panic) via unspecified vectors.
Проблемный код находился в функции devzvol_readdir и выглядел следующим образом:
static int devzvol_readdir(....)
{
....
char *ptr;
....
ptr = strchr(ptr + 1, '/') + 1;
rw_exit(&sdvp->sdev_contents);
sdev_iter_datasets(dvp, ZFS_IOC_DATASET_LIST_NEXT, ptr);
....
}
Функция strchr возвращает указатель на первое вхождение символа, переданного в качестве второго аргумента. Однако функция может вернуть нулевой указатель в случае, если символ не был найден в исходной строке. Но этот факт был забыт или не учтён. В итоге к возвращаемому значению просто прибавляется 1, результат записывается в переменную ptr, и дальше с указателем работают «как есть». Если полученный указатель был нулевым, то, прибавив к нему единицу, мы получим невалидный указатель, проверка которого на неравенство NULL не будет означать его валидности. При определённых условиях этот код мог приводить к возникновению kernel panic.
PVS-Studio обнаруживает эту уязвимость при помощи диагностического правила V769, сообщая, что указатель, возвращённый функцией strchr, может быть нулевым, и при этом он портится (из-за прибавления 1):
V769 The 'strchr(ptr + 1, '/')' pointer in the 'strchr(ptr + 1, '/') + 1' expression could be nullptr. In such case, resulting value will be senseless and it should not be used.
Network Audio System
Network Audio System (NAS) — клиент-серверная система передачи звука с открытым исходным кодом, доступным на SourceForge. NAS работает как под Unix, так и под Windows.
Уязвимость, обнаруженная в этом проекте, имеет кодовое название CVE-2013-4258.
Описание CVE-2013-4258: Format string vulnerability in the osLogMsg function in server/os/aulog.c in Network Audio System (NAS) 1.9.3 allows remote attackers to cause a denial of service (crash) and possibly execute arbitrary code via format string specifiers in unspecified vectors, related to syslog.
Код выглядел следующим образом:
....
if (NasConfig.DoDaemon) { /* daemons use syslog */
openlog("nas", LOG_PID, LOG_DAEMON);
syslog(LOG_DEBUG, buf);
closelog();
} else {
errfd = stderr;
....
В этом фрагменте кода неправильно используется функция syslog. Объявление этой функции выглядит следующим образом:
void syslog(int priority, const char *format, ...);
Второй параметр должен быть строкой формата, а уже последующие — данные, требуемые этой строке. Здесь же строка формата отсутствует, а в качестве аргумента напрямую передаётся целевое сообщение (переменная buf). Это и стало причиной уязвимости, которая может привести к выполнению произвольного кода.
Если верить записи в базе SecurityFocus, уязвимость проявлялась в операционных системах Debian и Gentoo.
Что же PVS-Studio? PVS-Studio обнаруживает эту ошибку с помощью диагностического правила V618 и выдаёт предупреждение:
V618 It's dangerous to call the 'syslog' function in such a manner, as the line being passed could contain format specification. The example of the safe code: printf("%s", str);
Обнаруживать эту и подобные ошибки помогает реализованный в анализаторе механизм аннотирования функций и большое количество аннотаций — сейчас их количество превышает 6500 для C, C++ и 900 для C#.
Правильный вызов этой функции, закрывающий данную уязвимость, выглядит следующим образом:
syslog(LOG_DEBUG, "%s", buf);
Здесь используется строка формата "%s", что делает вызов функции syslog безопасным.
Ytnef (Yerase's TNEF Stream Reader)
Ytnef — программа с открытым исходным кодом, доступным на GitHub. Предназначена для декодирования TNEF потоков, например, созданных в Outlook.
За прошедшую часть 2017 года в ней был выявлен целый ряд уязвимостей, описанных здесь. Рассмотрим одну из записей CVE, описанных в этом списке — CVE-2017-6298.
Описание CVE-2017-6298: An issue was discovered in ytnef before 1.9.1. This is related to a patch described as «1 of 9. Null Pointer Deref / calloc return value not checked.»
Все исправленные места, в которых могло произойти разыменование нулевого указателя, имели приблизительно один и тот же вид:
vl->data = calloc(vl->size, sizeof(WORD));
temp_word = SwapWord((BYTE*)d, sizeof(WORD));
memcpy(vl->data, &temp_word, vl->size);
Во всех подобных местах уязвимости связаны с неправильным использованием функции calloc. Данная функция может вернуть нулевой указатель в случае, если не удалось выделить запрашиваемый блок памяти. Но полученный указатель не проверяется на неравенство NULL, а используется из расчёта, что calloc всегда будет возвращать ненулевой указатель. Это несколько неосмотрительно.
Как PVS-Studio справляется с обнаружением подобных уязвимостей? Вполне себе спокойно: в анализаторе реализовано много различных диагностических правил, обнаруживающих работу с нулевыми указателями.
В частности, уязвимости, описанные выше, будут обнаружены с помощью диагностического правила V575. Предупреждение выглядит следующим образом:
V575 The potential null pointer is passed into 'memcpy' function. Inspect the first argument.
Анализатор обнаружил, что потенциально нулевой указатель, полученный в результате вызова функции calloc, передаётся в функцию memcpy без проверки на неравенство NULL.
Таким образом, PVS-Studio обнаружил и эту уязвимость. А ведь если бы анализатор регулярно использовался в процессе написания кода, то можно было бы предотвратить эту проблему ещё даже до того, как она попала бы в систему контроля версий…
MySQL
MySQL — свободная реляционная система управления базами данных. Обычно MySQL используется в качестве сервера, к которому обращаются локальные или удалённые клиенты, однако в дистрибутив входит библиотека внутреннего сервера, позволяющая включать MySQL в автономные программы.
Рассмотрим одну из уязвимостей, обнаруженных в данном проекте — CVE-2012-2122.
Описание CVE-2012-2122: sql/password.c in Oracle MySQL 5.1.x before 5.1.63, 5.5.x before 5.5.24, and 5.6.x before 5.6.6, and MariaDB 5.1.x before 5.1.62, 5.2.x before 5.2.12, 5.3.x before 5.3.6, and 5.5.x before 5.5.23, when running in certain environments with certain implementations of the memcmp function, allows remote attackers to bypass authentication by repeatedly authenticating with the same incorrect password, which eventually causes a token comparison to succeed due to an improperly-checked return value.
Вот так выглядит код, содержащий уязвимость:
typedef char my_bool;
my_bool
check_scramble(const char *scramble_arg, const char *message,
const uint8 *hash_stage2)
{
....
return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE);
}
Тип возвращаемого значения функции memcmp — int, а тип возвращаемого значения функции check_scramble — my_bool, фактически — char. В результате происходит неявное приведение int к char, при котором отбрасываются значения старших битов. Это приводило к тому, что примерно в 1 случае из 256 удавалось подключиться с любым паролем, зная имя пользователя. Ввиду того, что 300 попыток подключения занимали меньше секунды, эта защита настолько же хороша, как и её отсутствие. Подробнее про эту уязвимость можно почитать по ссылкам, упомянутым на странице CVE-2012-2122.
PVS-Studio обнаруживает эту проблему с помощью диагностического правила V642. Предупреждение выглядит следующим образом:
V642 Saving the 'memcmp' function result inside the 'char' type variable is inappropriate. The significant bits could be lost breaking the program's logic. password.c
Выходит, что и эту уязвимость удалось обнаружить, используя PVS-Studio.
iOS
iOS — мобильная операционная система для смартфонов, электронных планшетов и носимых проигрывателей, разрабатываемая и выпускаемая американской компанией Apple.
Рассмотрим одну из уязвимостей, которой была подвержена эта операционная система — CVE-2014-1266. К счастью, в общем доступе есть фрагмент кода, из которого видно, в чём же состояла проблема.
Описание уязвимости CVE-2014-1266: The SSLVerifySignedServerKeyExchange function in libsecurity_ssl/lib/sslKeyExchange.c in the Secure Transport feature in the Data Security component in Apple iOS 6.x before 6.1.6 and 7.x before 7.0.6, Apple TV 6.x before 6.0.2, and Apple OS X 10.9.x before 10.9.2 does not check the signature in a TLS Server Key Exchange message, which allows man-in-the-middle attackers to spoof SSL servers by (1) using an arbitrary private key for the signing step or (2) omitting the signing step.
Фрагмент кода, который привёл к возникновению уязвимости, выглядел следующим образом:
static OSStatus
SSLVerifySignedServerKeyExchange(SSLContext *ctx,
bool isRsa,
SSLBuffer signedParams,
uint8_t *signature,
UInt16 signatureLen)
{
OSStatus err;
....
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
goto fail;
goto fail;
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
goto fail;
....
fail:
SSLFreeBuffer(&signedHashes);
SSLFreeBuffer(&hashCtx);
return err;
}
Проблема заключается в двух операторах goto, расположенных рядом друг с другом. Только первый из этих операторов относится к оператору if, второй — нет. Таким образом, вне зависимости от значений предыдущих условий, будет осуществлён переход к метке fail. Из-за выполнения второго оператора goto значение err будет успешным. Это приводило к тому, что злоумышленники могли обманывать SSL сервера.
PVS-Studio обнаруживает эту проблему при помощи сразу двух диагностических правил — V640 и V779. Предупреждения выглядят следующим образом:
- V640 The code's operational logic does not correspond with its formatting. The statement is indented to the right, but it is always executed. It is possible that curly brackets are missing.
- V779 Unreachable code detected. It is possible that an error is present
Таким образом, анализатор предупреждает сразу о нескольких вещах, показавшихся ему подозрительными:
- логика работы программы не соответствует оформлению кода: судя по выравниванию, складывается впечатление, что оба оператора goto относятся к оператору if, но это не так. Первый оператор goto действительно находится под условием, второй — нет;
- наличие недостижимого кода: так как второй оператор goto выполняется безусловно, код, следующий за ним, выполняться не будет.
Выходит, что и тут PVS-Studio успешно справился со своей работой.
Эффективное использование статического анализа
Цель этой статьи, как я упоминал ранее – показать, что PVS-Studio успешно справляется с поиском уязвимостей. Подход, выбранный для достижения этой цели – демонстрация того, что анализатор успешно находит некоторые известные уязвимости. Материал, приведённый выше, был необходим для подтверждения того факта, что уязвимости можно искать с помощью статического анализа.
Теперь я бы хотел поговорить о том, как делать это наиболее эффективно. В идеале уязвимости должны обнаруживаться до того момента, как они станут таковыми (т.е. когда кто-нибудь их обнаружит и поймёт, каким образом можно эксплуатировать), и чем раньше они будут найдены, тем лучше. При правильном использовании статического анализа уязвимости можно обнаруживать ещё на этапе кодирования. О том, каким образом можно этого достичь, будет рассказано ниже.
Примечание. В этом разделе для единообразия я использую слово «ошибки». Но простые ошибки, как мы видели, могут быть потенциальными, а затем — действительными, уязвимостями. Прошу не забывать про это.
Вообще, чем раньше обнаружена и исправлена ошибка, тем дешевле стоимость её исправления. На рисунке, представленном ниже, приведены данные из книги Каперса Джонса (Capers Jones) «Applied Software Measurement».
Как видно из графиков, примерно 85% ошибок вносятся на этапе кодирования, при этом стоимость исправления на этом этапе минимальна. В то же время по мере того, как ошибка существует в программном коде, стоимость её исправления постоянно возрастает, и если на этапе кодирования устранение ошибки обойдется примерно в 25$, то после релиза программного продукта этот показатель увеличивается до десятков тысяч долларов. Про стоимость уязвимостей, обнаруженных после релиза, даже говорить не стоит.
Отсюда следует простой вывод — чем раньше обнаружена и исправлена ошибка, тем лучше. Целью статического анализа и является как можно более раннее обнаружение ошибок в коде. Статический анализ не является заменой других средств валидации и верификации программного обеспечения, а дополняет их.
Как же извлечь максимальную выгоду от использования статического анализатора? Первое правило — код должен проверяться регулярно. В идеале, ошибка должна быть исправлена ещё на этапе написания кода, до того, как будет заложена в систему контроля версий.
Тем не менее, запускать постоянные проверки на машине разработчика может быть неудобно. К тому же операция проверки всего кода может быть достаточно долгой, что не позволит часто перепроверять код после правок. В PVS-Studio для этого реализован режим инкрементального анализа, который позволяет анализировать только тот код, который был изменён/затронут с момента последней сборки. Более того, эта функция позволяет запускать анализ автоматически после сборки, что дает возможность разработчику не отвлекаться на самостоятельный запуск анализа. После завершения анализа, в случае, если в изменённых файлах были обнаружены ошибки, программисту будет выдана соответствующая нотификация.
Даже при условии такого использования возможно попадание ошибок в систему контроля версий. Поэтому нужно иметь и «второй уровень защиты» — использование статического анализатора на сборочном сервере. Например, интегрировать анализ кода в процесс ночных сборок. Это позволит регулярно проверять проекты по ночам, а утром получать сведения о том, какие ошибки попали в систему контроля версий. Важным моментом в этом случае является необходимость быстрого исправления ошибок, обнаруженных таким образом — желательно сразу же на следующий день. Иначе со временем на появление новых ошибок просто никто не будет обращать внимания, и толку от таких проверок не будет.
Внедрение статического анализа в процесс разработки может показаться нетривиальной задачей, если проект не разрабатывается с нуля. О том, как правильно начать использовать статический анализ в таком случае, написано в статье "Как быстро внедрить статический анализ в большой проект?"
Заключение
Надеюсь, мне удалось показать вам, что:
- даже простой, казалось бы, баг может являться серьёзной уязвимостью;
- PVS-Studio успешно справляется не только с поиском ошибок в коде, но и с поиском CWE и CVE.
И если стоимость простой ошибки в коде значительно возрастает со временем, то про стоимость уязвимостей и говорить нечего… При этом, если использовать статический анализ, многие уязвимости можно исправить ещё даже до того, как они попадут в систему контроля версий, не говоря уже о том, чтобы их кто-то находил и эксплуатировал.
Напоследок хочу посоветовать попробовать PVS-Studio на своём проекте — вдруг вам удастся найти что-то интересное, что убережёт ваш проект от попадания в базы CVE.