Научная статья на тему 'Автоматическое обнаружение дефектов программных систем на основе метода проверки модели'

Автоматическое обнаружение дефектов программных систем на основе метода проверки модели Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
172
23
i Надоели баннеры? Вы всегда можете отключить рекламу.

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Ицыксон Владимир Михайлович, Захаров Алексей Владимирович, Ахин Марат Халимович, Мяснов Александр Владимирович

Разработан комплексный подход к обнаружению программных дефектов, основывающийся на расширении статического анализа методом проверки моделей. Показана применимость подхода на примере задачи обнаружения ошибок протоколов вызова.

i Надоели баннеры? Вы всегда можете отключить рекламу.

Похожие темы научных работ по компьютерным и информационным наукам , автор научной работы — Ицыксон Владимир Михайлович, Захаров Алексей Владимирович, Ахин Марат Халимович, Мяснов Александр Владимирович

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Complex technique for software defect detection, based on model checking enhanced static analysis, is introduced in this article. Its usability is proven through function call protocol

Текст научной работы на тему «Автоматическое обнаружение дефектов программных систем на основе метода проверки модели»

СПИСОК ЛИТЕРАТУРЫ

1. Static analysis for security / В. Chess. G. McGraw // IEEE Security & Privacy. - IEEE. 2004. - Volume 2, Issue 6. - p. 76-79.

2. Брукс Ф. Мифический человеко-месяц или как создаются программные системы. - Символ-Плюс, 2006. - 304 с.

3. First Steps in the Verified Software Grand Challenge /J. Woodcock // IEEE Computer. - IEEE. 2006. -Volume 39, Number 10, p. 57-64.

4. Software Analysis: a Roadmap] / D. Jackson, M. Rinard // Proceedings of the Conference on The

Future of Software Engineering. - Limerick. Ireland: ACM. 2000.-p. 133-145.

5. Secure Coding: Principles & Practices / Mark G. Graff. Kenneth R. Van Wyk. - OReilly. 2003. - 200 p.

6. Principles of Program Analysis/Flemming Niel-son. Hanne R. Nielson. Chris Hankin. - Corr. 2nd printing. - Berlin [etc.]: Springer. 2005. - XXI. 452 p., 56 illus.

7. http://aivt.ftk.spbstu.ru/projects/gk2008/tests/ nov08

Ицыксон В.М., Захаров A.B., Ахин М.Х., Мясное A.B.

Автоматическое обнаружение дефектов программных

систем на основе метода проверки модели

Введение

В настоящее время одной из основных особенностей большинства программных систем (ПС) является их сложность. В некоторых областях объемы исходных кодов ПС могут достигать десятков миллионов строк. Такие объемы и сложная структура ПС, сложность языков программирования могут приводить к появлению ошибок в ПС. Особое место занимает специальный класс ошибок - программные дефекты - ошибки, приводящие к деградации программного обеспечения.

Программные дефекты, как правило, возникают на этапе кодирования по следующим причинам:

• неправильная интерпретация семантики языковых конструкций;

• некорректное использование системных и/или библиотечных функций;

• использование программных объектов и ресурсов без соблюдения требуемых протоколов вызовов;

• и др.

Дефекты в ПС в процессе работы могут приводить к отказам ПС. а также могут быть причиной возникновения потенциальных уяз-вимостей, тем самым значительно ухудшая их безопасность и надежность. То есть программные дефекты являются причиной снижения качества ПС [1].

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

Состояние предметной области

Выделяют две основные группы методов обнаружения дефектов: на базе статического или динамического анализа [2].

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

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

семейством динамических подходов является учет всех возможных путей выполнения программы и возможность его использования на любых этапах разработки. Также статический анализ не требует выполнения программы, что позволяет использовать его до пояааения первой работающей версии, тем самым сокращая расходы на устранение дефектов за счет уменьшения времени между появлением и обнаружением программных ошибок. К недостаткам относятся потенциальное наличие большого числа ложных срабатываний и значительная вычислительная сложность, в том числе из-за наличия различных неопределенностей времени выполнения относительно поведения программы (неопределенных условий, рекурсий, исключений, полиморфизма, и т.д.). Это усложняет использование статического анализа для крупных проектов.

Оба подхода, описанные выше, показывают удовлетворительные результаты при поиске определенных типов дефектов, таких как выход за границы массива, ошибки инициализации и т.п. В то же время для обнаружения важной группы дефектов, связанной с корректной последовательностью вызовов определенных функций, так называемых ошибок протоколов вызовов, использование таких подходов не позволяет достичь приемлемых результатов.

Ошибки протоколов вызовов могут приводить к утечкам ресурсов, нарушениям доступа к памяти, непредсказуемому поведению программы. зацикливанию, зависанию и пр. Для обнаружения подобных дефектов наиболее эффективным является использование аппарата темпоральной логики и основанного на нем метода проверки моделей (model checking) [3].

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

Первая из указанных задач - автоматическое построение модели программы - является самостоятельной сложной задачей. Существует множество работ, посвященных этой проблеме, однако полного законченного решения для реальных языков программирования не существует. Основные причины этого следующие:

• отсутствие формальной семантики для большинства языков программирования:

• принципиальные различия языков программирования и входных языков верификаторов;

• необходимость обеспечения адекватности модели;

• сложность моделирования некоторых типов данных языка программирования на языке верификатора;

• необходимость применения абстракции для построения такой модели реальной ПС. которая может быть верифицирована за приемлемое время с использованием ограниченных вычислительных ресурсов.

Тем не менее, к настоящему времени уже создано несколько подходов и программных средств, позволяющих в той или иной степени решить указанную задачу. Для построения моделей программ на языке Java существуют проекты Java2Spin в составе JCAT [4], Java PathFinder 1 [5]. Bandera [6]; для языка С - modex в составе Fea Ver [7], c2sdl [8].

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

Настоящая статья посвящена рассмотрению второй задачи - обнаружению дефектов с помощью метода проверки моделей.

Метод проверки моделей для обнаружения ошибок протоколов вызовов

В данной статье рассматривается подход на основе метода проверки моделей в применении к обнаружению дефектов, связанных с нарушением корректного протокола работы с библиотеками, модулями, компонентами в исходном коде программ на языке С. Нарушение корректного протокола работы может приводить. например, к утечке ресурсов, ошибкам работы с памятью и аварийному завершению приложения. В этой статье рассматриваются дефекты в программах на языке С, так как язык С является одним из самых используемых языков программирования, который часто применяется для разработки системного программного обеспечения, а также для построения критически важных систем. Однако предложенный подход может быть легко адаптирован и для других языков программирования высокого уровня.

В данной работе для проверки модели используется свободно-распространяемый верификатор Spin [9]. в котором требования спецификации представляются в виде формул линейной темпоральной логики. Выбор верификатора Spin не накладывает ограничений на разрабатываемый подход, но значительно облегчает создание модели. В данном верификаторе используется язык Promela. выразительная мощность которого несколько лучше языков других верификаторов подходит для построения моделей программ на языке С: близкая система типов, наличие конструкций ветвления и циклов и т.п.

Цель предлагаемого подхода - автоматическая проверка корректности протокола вызовов функций в ПС на языке С. Основные положения предлагаемого подхода:

• построение модели ПС осуществляется с использованием статического анализа исходного кода ПС на языке С;

• для описания семантики анализируемых функций используются внешние аннотации:

• спецификация корректного протокола вызовов функций выражается с помощью формул линейной темпоральной логики:

• метод проверки модели применяется для обнаружения дефектов и получения трассы.

Для построения модели используется статический анализ исходного кода программы, который включает следующие этапы:

• лексический анализ, результатом которого является последовательность токенов;

• синтаксический анализ, в результате которого строится абстрактное синтаксическое дерево;

• построение графа потока управления;

• построение модели программы на языке верификатора Spin.

Аннотации служат для спецификации анализируемого набора функций. Использование аннотаций позволяет однозначно идентифицировать требуемые функции в исходном коде ПС, а также создавать модель указанных функций на языке верификатора.

В результате применения статического анализа производится построение модели программы на входном языке верификатора Spin, при этом

• неинтересующие программные объекты и типы удаляются из модели;

• интересующие программные объекты представляются с помощью переменных встроенных типов языка Promela;

• указатели моделируются с помощью переменных типа int языка Promela: присваивание указателю другого указателя или арифметические операции с указателями моделируются соответствующими операциями над целочисленными переменными модели;

• операции разадресации указателей моделируются индексированием в массиве указателей;

• вызов функций моделируется запуском процессов, передача возвращаемого значения -с помощью каналов;

• ветвления моделируются операторами ветвления языка Promela;

• циклы моделируются циклами языка Promela;

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

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

На рисунке 1 приведена общая схема процесса анализа программной системы на основе предлагаемого подхода к обнаружению дефектов.

Анализируемый набор функций, как правило, предназначен для решения определенного круга задач, например файловый ввод-вывод, работа с динамической памятью, построение изображений, многопоточное исполнение, синхронизация потоков исполнения, передача данных по сети и т.д. Для решения указанных задач данным набором функций используются общие программные объекты, которые с точки зрения программного интерфейса могут выглядеть как целочисленные дескрипторы, указатели, структуры. Чаще всего для представления ресурсов используются дескрипторы и указатели, поэтому в данной статье рассматриваются именно эти способы представления. В таких наборах функций можно выделить функции-генераторы, функции-пользователи и функции-уничтожители ресурсов некоторого типа. Для моделирования

Рис. 1. Общая схс подобных функций предлагается использовать следующий подход.

Количество доступных ресурсов задается параметром модели N. Для учета выделенных ресурсов создается массив соответствующей размерности N. в котором ведется учет занятых ресурсов. При выделении ресурса в модели функции-генератора выполняется поиск первого свободного ресурса в массиве. Если свободный ресурс найден, он отмечается как используемый и возвращается как результат работы функции. Б зависимости от типа ресурса вводится биективное отображение индекса в массиве на множество допустимых значений указателя или дескриптора, например:

Л0-1+ 1,

/. (/) = /-1.

оор

Если свободный ресурс не найден, возвращается код ошибки.

В модели функции-пользователя ресурсов с помощью /ойр получается индекс / в массиве, проверяется, что значение индекса лежит в диапазоне массива, и данный ресурс отмечается как выделенный.

В модели функции-уничтожителя ресурсов с помощью /о6|1 получается индекс /' в массиве, проверяется, что значение индекса лежит в диапазоне массива, ресурс по индексу /' был вьщелен, после чего ресурс освобождается.

а процесса анализа

Верификатор Spin позволяет в формулах линейной темпоральной логики оперировать только глобальными переменными. Для формализации свойства "была вызвана функция/' предлагается ввести глобальную переменную cmd'. при моделировании вызова функции в начале выполнения соответствующего процесса переменной cmd присваивается константа op_f. в конце выполнения - значение opjnit.

Тогда для проверки того, что ресурс i был выделен функцией f и освобожден функцией g, можно использовать следующую обобщенную формулу темпоральной логики: ndefme APf ((cmd == op J) && (resfij >0)) Udefine APg ((cmd == op_g) && (resfij == 0)). [] (APf-> (<>APg)) где

• APf-атомарный предикат, выражающий, что ресурс был выделен;

• APg - атомарный предикат, выражающий, что ресурс был освобожден;

• res - массив ресурсов;

• [] - оператор всеобщности (G);

• <> - оператор "когда-то в будущем" (F):

• -> - импликация.

Пример применения подхода для функции работы с динамической памятью

Для иллюстрации работоспособности разработанного подхода применим его для

стандартных функций работы с памятью. Для простоты возьмем сокращенный набор функций управления памятью: malloc, realloc, free.

• Функция malloc выделяет память указанного размера. При успешном завершении выполнения функции возвращается указатель на выделенную память; в противном случае возвращается нулевой указатель.

• Функция realloc изменяет размер выделенной области памяти. В функцию передается указатель на выделенную ранее (с помощью malloc или realloc) область памяти и новый размер выделяемой памяти в байтах. Переданный указатель может быть нулевым. Функция возвращает указатель на вновь выделенную область памяти, при этом он может совпадать или не совпадать с ранее выделенным. Если не удается выделить запрошенное количество байт, функция возвращает NULL.

• Функция free освобождает выделенную ранее память.

Можно выделить следующие возможные ошибки, связанные с рассмотренным набором функций.

1. Разыменование нулевого указателя. Функции malloc и realloc могут вернуть указатель

NULL, который затем может быть ошибочно разыменован.

2. Разыменование указателя, указывающего на непринадлежащую программе память.

3. Утечка памяти. Память, выделенная с помощью malloc и/или realloc. не освобождается после завершения работы с ней.

4. Многократное освобождение памяти. Многократный вызов /гее к одному и тому же указателю.

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

Для демонстрации работоспособности предложенного подхода был разработан пример программы на языке С, в котором специально нарушается естественный порядок вызова библиотечных функций работы с динамической памятью [10].

На основе данной программы была построена модель на языке Píamela [10].

Для моделирования функций malloc и free используются специальные процессы с соответствующими именами. Процесс malloc

Рис. 2. НКА для протокола работы с malloc. realloc w free

моделирует выделение памяти. Возвращаемый указатель является индексом в массиве указателей, в котором отмечается, что память по данному индексу выделена. При удалении указателя с помощью оператора проверки (assert) обеспечивается защита от повторного освобождения памяти.

Темпоральное правило, которое необходимо проверить, - указатель, выделенный с помощью malloc, по всем возможным путям вычислений будет освобожден при помощи вызова free, - выражается следующей формулой темпоральной логики:

#define т ((and == opjnalloc) && (ptr[ij > 0)) Udefhie f ((cmd == op Jree) && (ptr[i] == 0) ).

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

(] (m->(<>/))

Построенная модель тестовой программы и указанная формула темпоральной логики были поданы на вход верификатора Spin. В результате проверки данной модели программы была определена невыполнимость указанной выше темпоральной формулы, что говорит о наличии дефекта (ошибки протокола вызова) в данной программе. Место и условия проявления ошибки можно идентифицировать путем анализа полученного контр-примера.

Правило, проверяющее, что функция free никогда не будет вызвана для невыделенного ресурса, можно выразить следующей формулой:

#dejbie free_unalIocated ((and == opJ'ree) && (sec == secjbefore) && (free_arg == (i+J)) && (ptrfij == 0)).

[ ] (!free_unallocated)

В этой формуле вводится понятие секции (sec == sec_before). Это позволяет указать, что перед выполнением функции free должны быть проверены остальные условия формулы: для указателя, переданного в виде параметра функции free, ресурс по соответствующему индексу не выделен.

Построенная модель и рассматриваемая формула были проверены с помощью Spin. В результате был получен контр-пример. показывающий наличие дефекта.

Пример применения подхода для функций работы с файлами

Второй пример иллюстрирует применимость подхода для анализа функций работы с файлами. Рассмотрим сокращенный набор функций, входящий в библиотеку "stdio.li": fopen, fprintf, fclose.

• Функция fopen открывает файл с заданным именем и режимом доступа. При невозможности открытия файла функция возвращает нулевой указатель.

• Функция fprintf выводит в файл отформатированную строку. При успешном завершении выполнения возвращается количество записанных байт, при ошибке - отрицательное значение.

• Функцияfclose закрывает файл. Значение 0 соответствует корректному закрытию файла, значение EOF - ошибке работы с файлом.

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

f=fopen(...)

Y

fclose(f, ...)

Рис. 3. НКА для функций fopen. fprintf, fclose

Для демонстрации работоспособности предложенного подхода был разработан пример программы на языке С, в котором специально нарушается естественный порядок вызова функций работы с файлами [10].

По рассматриваемой программе на языке С может быть построена соответствующая модель на языке Promela [10].

В данном примере рассмотрено множество функций, используемых для протоколирования программы. Функция logjnit инициализирует внутренние переменные протоколирования; функция log_message используется для вывода сообщений в файл протокола; функция log_deinit освобождает ресурсы.

В построенной модели функция fopen представляется процессом fopen, в котором возвращаемый функцией fopen указатель на структуру FILE моделируется индексом в

массиве ресурсов. По индексу в массиве отмечается. что ресурс был выделен (файл открыт). Процесс fclose сначала выполняет проверку того, что освобождаемый ресурс действительно был выделен, после чего моделирует освобождение ресурса - обнуление соответствующего элемента массива.

Правило, которое необходимо проверить, -указатель, выделенный с помощью fopen, по всем возможным путям выполнения рано или поздно будет освобожден при помощи fclose, может быть задано следующей формулой темпоральной логики:

Udefine fo ((cmd == op Jopen) && (fdesfi] > 0))

#define fc ((cmd == opJ~clo.se) && (fdes[i] ==

0))

[] (fo-> (<>fc)).

В результате проверки для данной модели программы верификатор Spin выдает контрпример, который может быть проанализирован для идентификации места и условия возникновения дефекта.

Заключение

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

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

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

Одним из направлений развития описанного подхода является его адаптация для решения задачи обнаружения дефектов в многопоточных приложениях, которые обычно плохо поддаются классическому статическому анализу. Использование подхода на основе проверки моделей позволяет справиться с этой проблемой.

Исследование выполнено в рамках работ по государственному контракту № 02.514.11.4081 "Исследование и разработка системы автоматического обнаружения дефектов в исходном коде программного обеспечения" Федерального агентства по науке и инновациям в рамках Федеральной целевой программы "Исследования и разработки по приоритетным направлениям развития научно-технологического комплекса России на 2007-2012 годы".

СПИСОК ЛИТЕРАТУРЫ

1. ISO/I ЕС 9126-1:2001

2. Software Analysis: a Roadmap / D. Jackson, M. Rinard // Proceedings of the Conference on the Future of Software Engineering. - 2000. - p. 133-145

3. Верификация моделей программ: Model Cheeking / Э. Кларк. О. Грамберг. Д. Пелед. - М. : МЦНМО. 2002.

4. Modeling and Validation of Java Multithreading Applications Using SPIN / C. Demartini. R. Iosif. R. Sisto// In Proc. 4th International SPIN Workshop. -1998

5. Model Checking Java Programs Using Java Path-Finder / K. Havclund. T. Pressburger // International Journal on Software Tools for Technology Transfer (STTT). - 2000

6. Bandera: Extracting Finite-state Models from Java Source Code/J. C. Corbett. M. B. Dwyer.J. Hat-cliff, S. Laubach et al. // In Proc. of the 22st International Conference on Software Engineering. - 2000

7. Automating Software Feature Verification / G.J. Holzmann, M.H. Smith // Bell Labs Technical Journal. - 2000. - Volume 5. Number 2. - p.72-87

8. Formal Verification of Software Source Code Through Semi-Automatic Modeling / C. Eisner // Software and Systems Modeling (SOSYM). - 2005. -Volume 4. Number 1

9. http://www.spinroot.com/

10. http://aivt.ftk.spbstu.ru/projects/gk2008/vcrifi-cation/samples/

i Надоели баннеры? Вы всегда можете отключить рекламу.