20
Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения 64-битных программ Евгений Рыжков, октябрь 2008

Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Embed Size (px)

Citation preview

Page 1: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Правила статического анализа кода для диагностики потенциально опасных конструкций с

точки зрения 64-битных программ

Евгений Рыжков, октябрь 2008

Page 2: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Аннотация В статье сформулированы правила диагностики потенциально опасных синтаксических конструкций в

исходном коде программ на языке Си++. Описаны принципы построения статического анализатора исходного

кода, реализующего проверку указанных правил.

Введение Задача статического анализа исходного кода известна давно [1] и существуют традиционные методы ее

решения как с теоретической так и с практической точек зрения.

Вместе с тем развитие индустрии промышленной разработки программного обеспечения ставит перед

разработчиками статических анализаторов кода новые задачи. Речь идет о переносе кода (портированиии,

миграции) приложений на 64-битные платформы, поддержке параллельного программирования и так далее.

В этих задачах, стоящих уже перед многими программистами, возникает множество нюансов и проблем [2, 3].

В их диагностике могут помочь различные инструменты и методики [4].

В настоящей статье рассматривается один из подходов к диагностике проблем в коде 64-битных приложений.

А именно - разработка специализированного статического анализатора кода.

Page 3: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Статический анализатор кода состоит из двух частей:

компилятора переднего плана (front end compiler) - модуля, выполняющего разбор исходного кода,

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

набора правил диагностики потенциально опасных конструкций.

Под потенциально опасными конструкциями будем понимать такие конструкции в коде программ, которые

при переносе приложения на 64-битную платформу могут привести к некорректной работе программ. Следует

не путать их с дефектами [5] в коде программ, которые являются ошибками и требуют исправления в любом

случае. В отличие от дефектов потенциально опасные конструкции, которые диагностирует статический

анализатор кода, должны быть просмотрены программистом. И уже программист принимает решение,

является ли данный код некорректным в конкретной ситуации. Если код признается программистом

некорректным, то он подлежит исправлению.

Таким образом, задача статического анализатора заключается в диагностике с помощью набора правил

потенциально опасных конструкций.

Page 4: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Разработка модуля анализа Принципы построения статического анализатора кода хорошо изучены и приведены в литературе [6]. Поэтому

для реализации анализатора, предназначенного для разработки 64-битных приложений, следует выбрать

традиционный подход к построению модуля анализа.

Так как разрабатываемый анализатор кода предназначен для языков Си и Си++, то исходя из знаний о типе

этих языков программирования и нужно конструировать модуль анализа.

Язык Си++ задается контекстно-свободной (КС) грамматикой (классификация по Хомскому). Для разбора

программ на языке Си++ используется синтаксический анализатор, распознающий КС-грамматику.

Лексический же разбор реализован на основе регулярной грамматики. Необходимость и лексического, и

синтаксического разборов объясняется особенностью проверяемых правил.

Распознавание языка Си++ реализуется, методом рекурсивного спуска (рекурсивный нисходящий анализ) с

возвратом. Такое распознавание реализовано в библиотеке анализа кода VivaCore [7].

В результате разбора кода получается синтаксическое дерево разбора (derivation tree). Дерево разбора по

сравнению с абстрактным синтаксическим деревом (abstract syntax tree) содержит больше информации,

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

осуществляет проход по дереву и выполняет проверку определенных правил.

Page 5: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Типы данных Прежде чем говорить о каких-либо правилах диагностики потенциально опасных конструкций, необходимо

определится с архитектурой, для которой будут разрабатываться правила. Для нас наиболее важна такая

составляющая архитектуры, как модель данных. Модель данных [2] - это соотношение размеров основных

типов данных на конкретной архитектуре. Так, модель данных 64-битной архитектуры Windows называется

LLP64. В то время как для 64-битной архитектуры Linux применяется модель LP64. В дальнейшем все правила

будут приводиться для архитектуры LLP64, однако они абсолютно также применимы и к архитектуре LP64

после замены определений основных базовых типов.

Введем множество T - множество всех целочисленных базовых и производных от них типов языка C++, в том

числе указателей. Примеры int, bool, short int, size_t, void*, указатели на классы.

Введем множество S - множество размеров этих типов (в байтах), такое что Tt Ss . Примеры: 1, 2, 4, 8,

16, 32, 64.

Количество элементов в множествах T и S различно, элементов в T больше чем в S .

Введем операцию соответствия 32 , при которой тип языка C++ отображается в рамках 32-битной архитектуры

в размер этого типа: SsSt 32 , а также операцию 64 , при которой тип языка отображается в рамках 64-

битой архитектуры в размер этого типа: SsSt 64 . Формально операции выглядят так: ST :32 и

ST :64 .

Page 6: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Введем множество T - множество всех memsize-типов (типов переменной размерности) языка C++, TT .

Примеры size_t, ptrdiff_t, int*, void*.

Элементы множества T обладают тем свойством, что

Tt :

SsSt

SsSt*

64

32 , *ss .

Другими словами, memsize-типы - это StStTtTT 6432:, .

Введем множество TT 32 - это все типы данных, являющиеся 32-битными в рамках как 32-битной, так и 64-

битной архитектуры, т.е. StStTtTT 64323232323232 :, . Пример: int.

По аналогии введем множество TT 64 - это все типы данных, являющиеся 64-битными в рамках как 32-битной,

так и 64-битной архитектуры. Пример: long long.

Page 7: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Размеры всех memsize-типов на 32-битной архитектуре равны одному числу q =4 (4 байта):

SpTt , верно что qSt 64 . Размеры всех memsize-типов на 64-битной архитектуре равны числу *q =8 (8

байт).

Введем множество P - типы данных "указатели" в языке C++, TP .

Введем операцию разыменования типа * следующим образом:

TP :* .

Данная операция предназначена для получения типа данных, на который указывает указатель: tp * .

Пример: intint** .

Введем множество D , состоящее из всех типов, производных от типа double. Пример: double, long double.

Page 8: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Правила анализа корректности кода Все правила анализа корректности кода представлены в виде функций, которые принимают некоторые

аргументы (разные для разных правил), а возвращают true в случае некорректного кода и false, в случае

корректного. Все правила составлены по результатам изучения и обработки ошибок переноса кода на 64-

битные платформы [2].

Приведение 32-битных целых типов к memsize-типам

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

memsize-типам.

Примеры:

unsigned a, c;

size_t b = a;

array[c] = 1;

. ,

, ,),(

2321

211иначеfalse

TtTtеслиtruettF

Page 9: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Приведение memsize-типов к целым 32-битным типам

Следует считать опасными конструкции явного и неявного приведения memsize-типов к целым типам

размерностью 32 бита.

Пример:

size_t a;

unsigned b = a;

. ,

, ,),(

3221

212иначеfalse

TtTtеслиtruettF

Page 10: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Memsize-типы в виртуальных функциях

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

а). Функция объявлена в базовом классе и в классе-потомке.

б). Типы аргументов функций не совпадают, но эквивалентны на 32-битной системе (например: unsigned,

size_t) и не эквивалентны на 64-битной.

Пример:

class Base {

virtual void foo(size_t);

};

class Derive : public Base {

virtual void foo(unsigned);

};

Page 11: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Рассмотрим кортежи 1M и 2M , представляющие собой наборы элементов из множества T . Ошибочной

следует считать ситуацию, при которой в 32-битном режиме кортежи 1M и 2M совпадают, а в 64-битном -

различны.

. ,

,..1,)()()()(

)()()()( ,

),(642641322321

642641322321

213

иначеfalse

niSmSmSmSm

SmSmSmSmеслиtrue

MMFiiii

iiii

Page 12: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Memsize-типы в перегруженных функциях

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

должны быть перегружены для целых 32-х и 64-битных типов данных.

Пример:

void WriteValue(__int32);

void WriteValue(__int64);

...

ptrdiff_t value;

WriteValue(value);

Page 13: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Рассмотрим вызов функции c n фактическими аргументами. Если существует 2 или более перегруженных

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

A - кортеж типов фактических параметров функции;

1A - кортеж типов формальных параметров первой перегруженной функции;

2A - кортеж типов формальных параметров второй перегруженной функции;

. ,

,..1,)()()()()( ,),,(

641322642321

214иначеfalse

niTaTaTaTaTaеслиtrueAAAF

iiiii

Page 14: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Приведение типов указателей на memsize-типы

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

32-/64-битный тип, а другой на memsize-тип.

Пример:

int *array;

size_t *sizetPtr = (size_t *)(array);

. ,

,

,

),(64322

*

11

*

2

64322

*

21

*

1

215

иначеfalse

TTtpTtp

TTtpTtpеслиtrue

ppF

Приведение memsize-типов к double

Опасным следует считать явные и неявные приведения memsize-типа к double и наоборот.

Пример:

size_t a;

double b = a;

. ,

, ,),(

1221

216иначеfalse

TtDtTtDtеслиtruettF

Page 15: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Memsize-типы в функции с переменным количеством аргументов

Опасным следует считать передачу memsize-типа (кроме указаталей) в функцию с переменным количеством

аргументов.

Пример:

size_t a;

printf("%u", a);

Пусть K - кортеж всех фактических типов, которые являются параметрами функции с переменным

количеством аргументов. Пусть функция вызывается с m аргументами.

. ,

,..1,/ ,)(7

иначеfalse

miPTесли ktrueKF

i

Page 16: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Опасные константы

Опасным следует считать использование констант определенного вида. Введем множество N целых чисел,

которые можно записать средствами языка C++. Введем множество "опасных" констант NC . Примеры

"опасных" констант: 4, 32, 0xffffffff и т.д.

. ,

, ,)(8

иначеfalse

CcеслиtruecF

Page 17: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Memsize-типы в объединениях

Опасным следует считать наличие в объединениях (union) членов memsize-типов.

Пример:

union PtrNumUnion {

char *m_p;

unsigned m_n;

} u;

Все типы данных, входящие в union будем называть кортежем U .

. ,

, ,)(9

иначеfalse

TUеслиtrueUF

Page 18: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Исключения и memsize-типы

Опасным следует считать бросание и обработку исключений с использованием memsize-типов.

Пример:

char *p1, *p2;

try {

throw (p1 - p2);

}

catch (int) {

...

}

. ,

, ,)(10

иначеfalse

Tесли ttruetF

Page 19: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Заключение Рассмотренные в статье правила диагностики потенциально опасных конструкций с точки зрения 64-битных

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

Однако в настоящий момент они реализованы в полном виде лишь в анализаторе кода Viva64

(www.viva64.com). Программный продукт Viva64 обеспечивает диагностику ошибок, специфичных для 64-

битных Windows приложений. Viva64 представляет собой lint-подобный статический анализатор Си/Си++ кода.

Инструмент Viva64 интегрируется в среду разработки Visual Studio 2005/2008 и предоставляет удобный

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

Page 20: Правила статического анализа кода для диагностики потенциально опасных конструкций с точки зрения

Литература 1. Scott Meyers, Martin Klaus "A First Look at C++ Program Analyzers.", 1997,

http://www.aristeia.com/ddjpaper1_frames.html.

2. Андрей Карпов, Евгений Рыжков. 20 ловушек переноса C++-кода на 64-битную платформу. RSDN

Magazine #1-2007. стр. 65 - 75.

3. Алексей Колосов, Евгений Рыжков, Андрей Карпов. 32 подводных камня OpenMP при

программировании на C++. RSDN Magazine #2-2008. стр. 3 - 17.

4. E. А. Рыжков, А.Н. Карпов. Подходы к верификации и тестированию 64-битных приложений.

"ИНФОРМАЦИОННЫЕ ТЕХНОЛОГИИ" N7, 2008, Стр. 41 - 45.

5. Макконнелл С. Совершенный код. Мастер-класс / Пер. с англ.- М.: Издательство "Русская редакция",

СПб.: Питер, 2007.- 896 с.: ил.

6. Системное программное обеспечение / А.В. Гордеев, А.Ю. Молчанов. - СПб.: Питер, 2002. - 736 с:.ил.

7. Евгений Рыжков, Андрей Карпов. Сущность библиотеки анализа кода VivaCore. RSDN Magazine #1-2008.

стр. 56 - 63.