﻿# PVS\-Studio: поиск дефектов безопасности

Анализатор PVS\-Studio всегда умел искать множество различных дефектов безопасности \(потенциальных уязвимостей\) в коде программ\. Однако, исторически сложилось, что мы позиционировали PVS\-Studio как инструмент для поиска ошибок\. Сейчас наблюдается мода на поиск в коде именно уязвимостей, а не ошибок, хотя на самом деле это одно и тоже\. Что же, значит пришло время провести ребрендинг нашего статического анализатора PVS\-Studio\. Начнём мы с Common Weakness Enumeration \(CWE\)\. В этой статье приводится таблица, сопоставляющая диагностические предупреждения PVS\-Studio с классификатором\. Таблица будет постепенно пополняться и изменяться, но уже сейчас с её помощью мы сможем писать статьи, посвященные обнаруженным дефектам безопасности в том или ином проекте\. Думаем, это привлечёт к нашему инструменту больше внимания специалистов, занимающихся безопасностью программного обеспечения\.

![0486_CWE_ru/image1.png](https://import.viva64.com/docx/blog/0486_CWE_ru/image1.png)

## Common Weakness Enumeration \(CWE\)

Для начала давайте разберемся с терминологией\. Для этого я процитирую фрагмент [FAQ](https://cwe.mitre.org/about/faq.html) с сайта cwe\.mitre\.org\.

**A1\. Что такое CWE? Что такое "дефект безопасности ПО"?**

  

Общий перечень дефектов безопасности ПО \(Common Weakness Enumeration, CWE\) предназначен для разработчиков и специалистов по обеспечению безопасности ПО\. Он представляет собой официальный реестр или словарь общих дефектов безопасности, которые могут проявиться в архитектуре, проектировании, коде или реализации ПО, и могут быть использованы злоумышленниками для получения несанкционированного доступа к системе\. Данный перечень был разработан в качестве универсального формального языка для описания дефектов безопасности ПО, а также в качестве стандарта для измерения эффективности инструментов, выявляющих такие дефекты, и для распознавания, устранения и предотвращения этих дефектов\.



Дефекты безопасности ПО \- это дефекты, сбои, ошибки, уязвимости и прочие проблемы реализации, кода, проектирования или архитектуры ПО, которые могут сделать системы и сети уязвимыми к атакам злоумышленников, если их вовремя не исправить\. К таким проблемам относятся: переполнения буферов, ошибки форматной строки и т\.д\.; проблемы структуры и оценки валидности данных; манипуляции со специальными элементами; ошибки каналов и путей; проблемы с обработчиками; ошибки пользовательского интерфейса; обход каталога и проблемы с распознаванием эквивалентности путей; ошибки аутентификации; ошибки управления ресурсами; недостаточный уровень проверки данных; проблемы оценки входящих данных и внедрение кода; проблемы предсказуемости и недостаточная "случайность" случайных чисел\.



**A2\. В чем разница между уязвимостью и дефектом безопасности ПО?**



Дефекты безопасности \- это ошибки, которые могут спровоцировать уязвимости\. Уязвимости, например, описанные в перечне общих уязвимостей и подверженностей воздействиям \(Common Vulnerabilities and Exposures, CVE\), \- это ошибки программы, которые могут быть непосредственно использованы злоумышленником для получения доступа к системе или сети\.

## Соответствие между предупреждениями PVS\-Studio и CWE

Нам хочется, чтобы анализатор PVS\-Studio начали воспринимать не только как инструмент поиска ошибок, но и как инструмент, который помогает сократить количество уязвимостей в коде\. Конечно, не каждый дефект безопасности, перечисленный в CWE, является уязвимостью\. Можно ли использовать тот или иной дефект для атаки, зависит от множества факторов\. Поэтому в дальнейшем мы будем писать, что анализатор PVS\-Studio выявляет не уязвимости, а потенциальные уязвимости\. Это будет более правильно\.

Итак, представляю первый вариант таблицы соответствий\. Таблица будет пополняться и уточняться, но даже первый вариант уже позволяет составить общее впечатление о возможностях анализатора\.

|\*\*CWE\*\*|\*\*PVS\\\-Studio\*\*|\*\*CWE Description\*\*|
|---|---|---|
|CWE\\\-14|V597|Compiler Removal of Code to Clear Buffers|
|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\\\-131|V514, V531, V568, V620, V627, V635, V641, V651, V687, V706, V727|Incorrect Calculation of Buffer Size|
|CWE\\\-134|V576, V618, V3025|Use of Externally\\\-Controlled Format String|
|CWE\\\-135|V518, V635|Incorrect Calculation of Multi\\\-Byte String Length|
|CWE\\\-188|V557, V3106|Reliance on Data/Memory Layout|
|CWE\\\-195|V569|Signed to Unsigned Conversion Error|
|CWE\\\-197|V642|Numeric Truncation Error|
|CWE\\\-36|V631, V3039|Absolute Path Traversal|
|CWE\\\-369|V609, V3064|Divide By Zero|
|CWE\\\-401|V701, V773|Improper Release of Memory Before Removing Last Reference \\\('Memory Leak'\\\)|
|CWE\\\-404|V611, V773|Improper Resource Shutdown or Release|
|CWE\\\-415|V586|Double Free|
|CWE\\\-416|V774|Use after free|
|CWE\\\-457|V573, V614, V670, V3070, V3128|Use of Uninitialized Variable|
|CWE\\\-462|V766, V3058|Duplicate Key in Associative List \\\(Alist\\\)|
|CWE\\\-467|V511, V512, V568|Use of sizeof\\\(\\\) on a Pointer Type|
|CWE\\\-468|V613, V620, V643|Incorrect Pointer Scaling|
|CWE\\\-476|V522, V595, V664, V757, V769, V3019, V3042, V3080, V3095, V3105, V3125|NULL Pointer Dereference|
|CWE\\\-478|V577, V719, V622, V3002|Missing Default Case in Switch Statement|
|CWE\\\-481|V559, V3055|Assigning instead of comparing|
|CWE\\\-482|V607|Comparing instead of Assigning|
|CWE\\\-483|V640, V3043|Incorrect Block Delimitation|
|CWE\\\-561|V551, V695, V734, V776, V779, V3021|Dead Code|
|CWE\\\-562|V558|Return of Stack Variable Address|
|CWE\\\-563|V519, V603, V751, V763, V3061, V3065, V3077, V3117|Assignment to Variable without Use \\\('Unused Variable'\\\)|
|CWE\\\-570|V501, V547, V560, V654, V3022, V3063|Expression is Always False|
|CWE\\\-571|V501, V547, V560, V617, V654, V694, V3022, V3063|Expression is Always True|
|CWE\\\-587|V566|Assignment of a Fixed Address to a Pointer|
|CWE\\\-588|V641|Attempt to Access Child of a Non\\\-structure Pointer|
|CWE\\\-674|V3110|Uncontrolled Recursion|
|CWE\\\-690|V522, V3080|Unchecked Return Value to NULL Pointer Dereference|
|CWE\\\-762|V611|Mismatched Memory Management Routines|
|CWE\\\-805|V512, V594, V3106|Buffer Access with Incorrect Length Value|
|CWE\\\-806|V512|Buffer Access Using Size of Source Buffer|
|CWE\\\-843|V641|Access of Resource Using Incompatible Type \\\('Type Confusion'\\\)|

Таблица N1\. Первый черновой вариант таблицы соответствий CWE и диагностик PVS\-Studio\.

Теперь мы сможем писать в [статьях о проверке проектов](https://pvs-studio.ru/ru/blog/inspections/), какие мы нашли потенциальные уязвимости в том или ином проекте\. Раз многие хвалятся, что их анализаторы выявляют дефекты безопасности, то и мы затронем эту тему в своих статьях\.

## Демонстрация

Давайте посмотрим, как приведенную выше таблицу нам можно использовать при написании статей\. Проанализируем проект и посмотрим на диагностические сообщения PVS\-Studio с точки зрения дефектов безопасности\.

Конечно, далеко не каждый проект стоит изучать с точки зрения уязвимости\. Поэтому давайте возьмем такой серьезный проект, как Apache HTTP Server\. 

Итак, проверяем Apache HTTP Server с помощью PVS\-Studio и видим, что баги лезут из всех щелей\. Стоп\! Теперь это не баги, а дефекты безопасности\! Намного солидней говорить про потенциальные уязвимости, чем про опечатки и ошибки\.

Сразу скажу, что в этот раз мы не будем анализировать проект целиком, так как перед нами стоит задача только показать использование таблицы на практике\. Остановимся на трех предупреждениях\.

**Пример N1**

```cpp
#define myConnConfig(c) \
(SSLConnRec *)ap_get_module_config(c->conn_config, &ssl_module)

....

int ssl_callback_alpn_select(SSL *ssl,
  const unsigned char **out, unsigned char *outlen,
  const unsigned char *in, unsigned int inlen,
  void *arg)
{
  conn_rec *c = (conn_rec*)SSL_get_app_data(ssl);
  SSLConnRec *sslconn = myConnConfig(c);
  apr_array_header_t *client_protos;
  const char *proposed;
  size_t len;
  int i;

  /* If the connection object is not available,
   * then there's nothing for us to do. */
  if (c == NULL) {
    return SSL_TLSEXT_ERR_OK;
  }
  ....
}
```

Анализатор PVS\-Studio выдаёт предупреждение: [V595](https://pvs-studio.ru/ru/docs/warnings/v595/) The 'c' pointer was utilized before it was verified against nullptr\. Check lines: 2340, 2348\. ssl\_engine\_kernel\.c 2340

С точки зрения дефектов безопасности это: **CWE\-476 \(NULL Pointer Dereference\)**

Суть ошибки\. Выделим две наиболее важные сточки кода:

```cpp
SSLConnRec *sslconn = myConnConfig(c);
if (c == NULL) {
```

Проверка _\(c \=\= NULL\)_ говорит нам, что указатель может быть нулевым\. Однако, он уже разыменовывался внутри макроса _myConnConfig_:

```cpp
#define myConnConfig(c) \
(SSLConnRec *)ap_get_module_config(c->conn_config, &ssl_module)
```

Таким образом, код никак не защищён от разыменовывания нулевого указателя\.

**Пример N2**

```cpp
int get_password(struct passwd_ctx *ctx)
{
  char buf[MAX_STRING_LEN + 1];
  ....
  memset(buf, '\0', sizeof(buf));
  return 0;
err_too_long:
  ....
}
```

Анализатор PVS\-Studio выдаёт предупреждение: [V597](https://pvs-studio.ru/ru/docs/warnings/v597/) The compiler could delete the 'memset' function call, which is used to flush 'buf' buffer\. The memset\_s\(\) function should be used to erase the private data\. passwd\_common\.c 165

С точки зрения дефектов безопасности это: **CWE\-14 \(Compiler Removal of Code to Clear Buffers\)**

Суть ошибки\. При компиляции кода в режиме оптимизации, компилятор удалит вызов функции _memset_, так как с точки зрения компилятора этот вызов лишний\. После заполнения нулями буфера, созданного на стеке, этот буфер более никак не используется\. Значит, заполнять буфер нулями \- это пустая трата времени и следует удалить вызов функции _memset_\. Таким образом, приватные данные не будут затерты и останутся в памяти\.

Хочу обратить внимание, что это не абстрактное теоретически возможное поведение компилятора\. Компиляторы действительно так делают, чтобы ускорить наши программы\. Подробности:

* [Перезаписывать память \- зачем?](https://pvs-studio.ru/ru/blog/posts/cpp/k0041/)
* [V597\. The compiler could delete the 'memset' function call, which is used to flush 'Foo' buffer\.](https://pvs-studio.ru/ru/docs/warnings/v597/)

**Пример N3**

```cpp
static int is_quoted_pair(const char *s)
{
    int res = -1;
    int c;

    if (((s + 1) != NULL) && (*s == '\\')) {
        c = (int) *(s + 1);
        if (apr_isascii(c)) {
            res = 1;
        }
    }
    return (res);
}
```

Анализатор PVS\-Studio выдаёт предупреждение: [V694](https://pvs-studio.ru/ru/docs/warnings/v694/) The condition \(\(s \+ 1\) \!\= \(\(void \*\) 0\)\) is only false if there is pointer overflow which is undefined behaviour anyway\. mod\_mime\.c 531

С точки зрения дефектов безопасности это: **CWE\-571 \(Expression is Always True\)**

Суть ошибки\. Условие _\(\(s \+ 1\) \!\= NULL\)_ всегда истинно\. Ложным оно может стать только при переполнении указателя\. Переполнение указателя приводит к неопределённому поведению программы, поэтому про такой случай говорить вообще смысла нет\. Можно считать, что условие всегда истинно, о чем и сообщил нам анализатор\.

Мы не авторы кода и точно не знаем, как должен выглядеть код, но, скорее всего, он должен быть таким:

```cpp
if ((*(s + 1) != '\0') && (*s == '\\')) {
```

## Заключение

Ура, анализатор PVS\-Studio может использоваться для выявления потенциальных уязвимостей кода\!

Предлагаем всем желающим подробнее познакомиться с анализатором кода PVS\-Studio и попробовать демонстрационную версию анализатора на собственных проектах\. Страница продукта: [PVS\-Studio](https://pvs-studio.ru/ru/pvs-studio/)\.

По всем техническим вопросам и вопросам лицензирования просим писать нам на почту support \[@\] viva64\.com или воспользоваться [формой обратной связи](https://pvs-studio.ru/ru/about-feedback/)\.