Особенности

При анализе программного обеспечения, разработанного на языках программирования C/C++, входными данными комплекса анализа исходных текстов Project Viewer, являются файлы препроцессора (как и случае работы компилятора), а не исходные файлы. Этот подход позволяет обеспечить высокую точность анализа.

При анализе для каждого файла исходного текста строится дерево синтаксического разбора, в котором все использования (вызовы) типов информационных объектов, функциональных объектов и информационных объектов связываются с их определениями. Данное решение позволяет проводить точный статический и динамический анализ, а также добавлять в Project Viewer различные диагностики для проверки анализируемого исходного кода.

Сформированные в результате анализа деревья синтаксического разбора образуют базу данных проекта и сериализуются (сохраняются) для быстрой загрузки проекта в будущем без необходимости проведения повторного анализа.

В дальнейшем возможна многократная загрузка (при необходимости – на разных СВТ) проанализированного проекта и запуск диагностик кода, добавленных в Project Viewer, а также проведение статического/динамического анализа и формирование отчетных материалов.

Диагностики, реализованные в комплексе анализа исходных текстов Project Viewer, разделены на несколько типов:

  1. Диагностики потока выполнения
  2. Диагностики с использованием базы опасных конструкций CWE
  3. Диагностики, специфичные для операционных систем семейств Unix/Linux
  4. Диагностики поиска избыточности исходных текстов

Точность сопоставления результатов динамического анализа с результатами статического анализа обеспечивается комплексом Project Viewer за счет следующих факторов:

  1. Динамический анализ выполняется с учетом потоков выполнения приложения (последовательность мониторов из каждого потока отдельно сопоставляется с результатами статического анализа).
  2. На каждом шаге сопоставления учитывается фактический уровень вложенности стека (выводимый вместе с монитором при протоколировании) и теоретический уровень вложенности стека (вычисляемый по маршрутам статического анализа)

Диагностики

Методика написания диагностического кода делится на четыре части:

  1. Компиляция исследуемого исходного кода
  2. Получение для него файлов препроцессора
  3. Построение деревьев синтаксического разбора (анализ проекта)
  4. Написание кода диагностики, который обходит синтаксические деревья и ищет в них интересующие конструкции

Ниже представлен фрагмент дерева, созданный комплексом Project Viewer для анализируемого кода.

Дерево разбора

Деревья синтаксического разбора, построенные в процессе анализа, позволяют находить потенциально опасные фрагменты кода. Например, функции с определенным названием, бесконечные циклы, опасные строковые литералы и т.д. Анализ файлов препроцессора с построением деревьев и последующей строгой линковкой (связывания использований с их определениями) позволил реализовать в комплексе Project Viewer следующие диагностики:

  1. Диагностики потока выполнения

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

    Диагностики потока выполнения
    if
    Таблица случаев некорректных использований конструкции if (условие if всегда false и т.п.)
    switch
    Таблица случаев некорректных использований конструкции switch, (switch используется без default и т.п.)
    for
    Таблица случаев некорректных (интересных) использований конструкции for, (цикл выполняется 0,1 или бесконечное количество раз и т.п.)
    do
    Таблица случаев некорректных (интересных) использований конструкции do (цикл выполняется 0,1 или бесконечное количество раз и т.п.)
    while
    Таблица случаев некорректных (интересных) использований конструкции while (цикл выполняется 0,1 или бесконечное количество раз и т.п.)
    And
    Таблица случаев некорректного использования логических операций (повторное условие и т.п.)
    Label
    Таблица меток на которых нет переходов
    Выражения
    Таблица некорректных выражений
    Определения
    Таблица некорректных определений (случаи, последовательной записи значения без использований между ними и т.п.)
    break
    Диагностика корректных использований break
    return
    Диагностика корректных использований return
    goto
    Диагностика корректных использований goto
    Ассемблер
    Таблица ассемблерных вставок
    Все диагностики
    Таблица результатов запуска всех перечисленных выше диагностик.
  2. Диагностики с использованием базы опасных конструкций CWE

    Диагностики данного типа позволяют выявлять опасные конструкции программного кода, содержащиеся в базе опасных конструкций CWE (http://cwe.mitre.org/). Ниже представлены описания основных видов уязвимостей.

    CWE-843 – нарушение типов данных
    Программа выделяет или инициализирует ресурс, например, указатель, объект или переменную с использованием одного типа, но позже, доступ к ресурсу осуществляется через тип, несовместимый с первоначальным. Доступ к ресурсу с помощью несовместимого типа, может вызвать логические ошибки и некорректную работу программы.
    CWE-464 – неуместное добавление структур
    cwe-464 В этом примере третий символ конвертируется функцией atoi. Если эта функция не сможет преобразовать строку в число и вернет 0, то последующие значения в массиве не будут напечатаны.
    CWE-481 – присваивание вместо сравнения / 482 – сравнение вместо присваивания
    Во многих языках программирования символы присваивания и сравнения похожи, и их несложно перепутать. Например, в конструкции «if» используется знак сравнения «==». Если программист вместо сравнения напишет присваивание «=», то синтаксической ошибки не произойдёт и код будет скомпилирован, однако может быть нарушена логика работы программы.
    CWE-587 – присвоение значения определенной ячейки памяти
    cwe-587 В этом примере указателю присваивается фиксированное место в памяти. Использование такого адреса не практично, так как адрес не будет действительным на всех средах или платформах.
    CWE-806 – ошибка при копировании массивов / 805 – неправильное вычисление длины массива
    cwe-806 В приведенном примере строка source копируется в dest функцией strncpy. Однако, в параметрах функции strncpy для определения количества символов для копирования используется размер массива source, а не dest. Это приведет к переполнению массива, так как размер строки исходного массива больше, чем в конечном.
    CWE-120 – переполнение массива
    cwe-120 Переполнение буфера – явление, возникающее, когда компьютерная программа записывает данные за пределами выделенного в памяти буфера. Переполнение буфера обычно возникает из-за неправильной работы с данными, полученными извне, и памятью, при отсутствии жесткой защиты со стороны подсистемы программирования (компилятор) и операционной системы. В результате переполнения могут быть испорчены данные, расположенные следом за буфером (или перед ним), а также может произойти аварийное завершение или зависание программы. Последствия доступа злоумышленника к уязвимому к переполнению буфера коду могут варьироваться от раскрытия конфиденциальных данных до полного захвата системы. Вышеприведенная программа не ограничивает размер имени, введенного пользователем. Если пользователь введет больше 20 символов, то произойдёт переполнение массива.
    CWE-415 – повторное использование функции free с одним и тем же аргументом
    cwe-415 Программа вызывает функцию free() дважды на один и тот же адрес памяти, что может привести к необратимым изменениям в определенных местах памяти. Это, в свою очередь, приводит к тому, что программа не будет работать корректно или к тому, что функция malloc будет дважды возвращать один и тот же указатель.
    CWE-122 – переполнение массива
    cwe-122 Для массива была выделена память, но нет гарантии того, что строка argv[1] уложится в выделенную память.
    CWE-191 – выход за предел значений integer
    cwe-191 При работе с присвоением переменным значений следует обратить внимание на предельные значения типов данных. Так, например, в данном примере после вычитания значение переменной станет 2147483647, что приведет к нарушению логики работы программы.
    CWE-484 – отсутствие break в конструкции switch
    Отсутствие break, там где он необходим, может привести к тому, что будет выполнен код, который не предусмотрен логикой работы программы.
    CWE-366 – конфликт потоков данных
    cwe-191 Ошибка проектирования многопоточной системы или приложения, при которой работа системы или приложения зависит от того, в каком порядке выполняются части кода. Как видно из примера, переменная counter объявляется статической, и если такой код будет выполняться в нескольких потоках, логика работы программы может быть нарушена.
    CWE-416 – использование аргумента функции free после ее вызова
    cwe-416 В вышеописанном примере видно, что аргумент, который передавался функции free, далее был использован. Последствия такого использования могут быть разные: от искажения данных до выполнения произвольного кода.
    CWE-242 – использование опасных функций
    Программа вызывает функцию, про которую нельзя абсолютно точно сказать, что она будет работать корректно. Возьмём к примеру функцию gets: cwe-242 В данной ситуации эта функция небезопасна, так как она просто копирует то, что ввел пользователь в массив без проверки его размера. Это может привести к переполнению массива.
    CWE-469 − использование вычитания указателей для определения размера
    Программа вычитает один указатель из другого, чтобы определить размер, но этот расчет может быть неправильным, если указатели не существует в том же фрагменте памяти.
    CWE-676 − использование потенциально опасных функций
    К потенциально опасным мы отнесли такие функции, как printf,sprintf,strcat,strcpy,memset. При некорректном использовании этих функций в коде появятся уязвимости.
    CWE-467 – использование sizeof для указателей
    В этом примере sizeof используется для определения размера указателя, что является бесполезной информацией и может вызвать некорректную работу программы. Для определения размера структуры данных необходимо использовать sizeof(*foo).
  3. Диагностики, специфичные для операционных систем семейств Unix/Linux Диагностики Unix/Linux
    • Поиск уязвимости авторизации связан с функцией chmod, с помощью которой можно изменить права доступа на файлы.
    • Kill – эта функция, завершающая процесс. При ее неправильном использовании есть риск того, что файловая система будет забита большим количеством временных файлов.
  4. Диагностики поиска избыточности исходных текстов

    Диагностики избыточности

    Комплекс Project Viewer создает следующие таблицы с избыточностью:
    • Избыточные файлы – в таблицу попадают файлы проекта, которые не используются при компиляции.
    • Избыточные типы – таблица типов данных, которые не используются в проекте.
    • Избыточные функции – совокупность функций, определенных, но не используемых в коде.
    • Блоки – таблица избыточных блоков исходного кода.
    • Выражения – таблица избыточных выражений в исходном коде.
    • Включения – таблица избыточных директив #include.
    • Ссылки – таблица избыточных ссылок.
    • Приведения – таблица избыточных приведений типов.