Share
Embed
Formal Verification of C code
"Формальная верификация кода на языке Си" Ефремов Д.В.
Доклад посвящен разработке корректного программного обеспечения с применением одного из видов статического анализа кода. Будут освещены вопросы применения подобных методов, их слабые стороны и ограничения, а также рассмотрены результаты, которые они могут дать. На конкретных примерах будет продемонстрировано, как выглядят разработка спецификаций для кода на языке Си и доказательство соответствия кода спецификациям.
Доклад представлен на конференции PHDays VII (2017) 24 мая в секции PDUG.
Denis
May 24, 2017
Transcript
90 slides
-
Заголовок ptsecurity.com Формальная верификация кода на языке Си м.н.с. Ефремов
Д.В. (аспирант ВШЭ) ИСП РАН [email protected] -
Заголовок • Hacker-Proof Code Confirmed (quantamagazine) • Computer Scientists Close
In on Perfect, Hack-Proof Code (wired) • Kaspersky Launches ‘Unhackable’ OS (guidingtech) • Unhackable kernel could keep all computers safe from cyberattack (newscientist) • Is This Security-Focused Linux Kernel Really UnHackable? (thehackernews) • Hack-resilient • Error-free code • Yale develops world's first hacker-resistant operating system (ibtimes) Немного заголовков -
Заголовок • Crowd Sourced Formal Verification (CSFV) (VERIGAMES) • High-Assurance
Cyber Military Systems (HACMS) • Formally Verify Blockchain-Based Integrity Monitoring System • A Diagnostic Approach for Persistent Threat Detection (ADAPT) • Cyber Fault-tolerant Attack Recovery (CFAR) • Testing and Modeling of Brandeis Artifacts (TAMBA) • Clean-slate design of Resilient, Adaptive, Secure Hosts (CRASH) DARPA -
Заголовок • Orange Book: division A (verified protection) (A1, Beyond
A1) • Common Criteria (EAL7) • DO-178C/DO-333 "Formal Methods Supplement to DO-178C and DO-278A” • IEC 61508 (SIL4) • ФСТЭК России ГОСТ Р ИСО/МЭК 15408 «Требованиях безопасности информации к операционным системам» профили защиты операционных систем общего назначения (типа «А») Стандарты -
Заголовок •Verified Software Initiative (2007) •Dafny •Whiley •SPARK/Ada •The Key
Project/Java •Spec#/Sing# Академический фронт -
Заголовок • Верификация - проверка соответствия программного обеспечения предъявляемым к
нему требованиям; • Дедуктивная верификация – представление корректности программы как набора математических утверждений, называемых условиями верификации, выполнение которых проверяется автоматическими или интерактивными доказателями теорем; • Спецификация - набор требований и параметров, которым удовлетворяет некоторый объект (представлена в виде мат. модели, тестовых наборов, формальной спецификации) Верификация -
Заголовок Высшая школа экономики, Москва, 2016 Дедуктивная верификация программ •
Лекция Алана Тьюринга Лондонскому математическому обществу • Методы Флойда/Хоара • Инструменты дедуктивной верификации для Си, Java, С# • SunRise, ESC/Java, Frama-C, LOOP, Boogie/VCC • Применение к реальным проектам небольшого размера • Атомная энергетика (Англия, Франция) • Авионика (Airbus, NASA) • Компоненты специализированных ОС (seL4, Hyper-V) -
Заголовок • Компилятор и линковщик работают корректным образом • В
программном обеспечении, использующемся при верификации, не произошло ошибок • Компьютер функционирует таким образом, как мы думаем об этом (rowhammer) • Нижележащий слой ПО (например, ОС, прошивка сетевой карты, микрокод процессора) функционирует в рамках нашего представления о том, что он должен делать и что не должен делать (и ещё не содержит ошибок) • Пользователь компьютера, если он есть, специально не «пакостит» • Выполнены предположения о входных данных программы, о начальном состоянии • … На что опираться vs. Что вы будете с этого иметь (1) -
Заголовок •Гарантии того, что программное обеспечение функционирует в точном соответствии
с требованиями, к нему предъявляемыми, на всех входных данных, начальных состояниях, при любом поведении окружения * ** • * В предположении что все предположения выполнены • ** И не осталось предположений, которых мы не занесли в списочек На что опираться vs. Что вы будете с этого иметь (2) -
Заголовок • Отсутствие деления на ноль 𝑏 ≠ 0 𝑐
≔ 𝑎 𝑏 {𝑎 = 𝑐 ∗ 𝑏} Примеры требований по безопасности (safety) -
Заголовок • Отсутствие деления на ноль 𝑏 ≠ 0 𝑐
≔ 𝑎 𝑏 {𝑎 = 𝑐 ∗ 𝑏} Примеры требований по безопасности (safety) -
Заголовок • Отсутствие деления на ноль 𝑏 ≠ 0 𝑐
≔ 𝑎 𝑏 {𝑎 = 𝑐 ∗ 𝑏} Примеры требований по безопасности (safety) -
Заголовок • Отсутствие деления на ноль 𝑏 ≠ 0 𝑐
≔ 𝑎 𝑏 {𝑎 = 𝑐 ∗ 𝑏} • Отсутствие целочисленного переполнения 𝐼𝑁𝑇_𝑀𝐼𝑁 ≤ 𝑎 + 1 ≤ 𝐼𝑁𝑇_MAX 𝑏 ≔ 𝑎 + 1 𝑎 = 𝑏 − 1 Примеры требований по безопасности (safety) -
Заголовок • Отсутствие деления на ноль 𝑏 ≠ 0 𝑐
≔ 𝑎 𝑏 {𝑎 = 𝑐 ∗ 𝑏} • Отсутствие целочисленного переполнения 𝐼𝑁𝑇_𝑀𝐼𝑁 ≤ 𝑎 + 1 ≤ 𝐼𝑁𝑇_MAX 𝑏 ≔ 𝑎 + 1 𝑎 = 𝑏 − 1 • Отсутствие разыменования нулевого указателя \valid 𝑎 ∗ 𝑎 ≔ 1{∗ 𝑎 = 1} • … Примеры требований по безопасности (safety) -
Заголовок • Массив отсортирован ∀𝑖𝑛𝑡 𝑗; 0 ≤ 𝑗 <
𝐴𝑅𝑅𝐴𝑌_𝑆𝐼𝑍𝐸 − 1 ⇒ 𝑎 𝑗 ≤ 𝑎[𝑗 + 1] • Функция возвращает всегда положительное значение \ 𝑟𝑒𝑠𝑢𝑙𝑡 > 0 Примеры требований по функциональности -
Заголовок • Массив отсортирован ∀𝑖𝑛𝑡 𝑗; 0 ≤ 𝑗 <
𝐴𝑅𝑅𝐴𝑌_𝑆𝐼𝑍𝐸 − 1 ⇒ 𝑎 𝑗 ≤ 𝑎[𝑗 + 1] • Функция возвращает всегда положительное значение \ 𝑟𝑒𝑠𝑢𝑙𝑡 > 0 • Функция может менять порядок элементов в массиве, но не его содержимое 0_0 Примеры требований по функциональности -
Заголовок • Массив отсортирован ∀𝑖𝑛𝑡 𝑗; 0 ≤ 𝑗 <
𝐴𝑅𝑅𝐴𝑌_𝑆𝐼𝑍𝐸 − 1 ⇒ 𝑎 𝑗 ≤ 𝑎[𝑗 + 1] • Функция возвращает всегда положительное значение \ 𝑟𝑒𝑠𝑢𝑙𝑡 > 0 • Функция может менять порядок элементов в массиве, но не его содержимое 0_0 • Если в дереве присутствует искомый элемент, то функциях его обязательно найдёт O_O Примеры требований по функциональности -
Заголовок • Массив отсортирован ∀𝑖𝑛𝑡 𝑗; 0 ≤ 𝑗 <
𝐴𝑅𝑅𝐴𝑌_𝑆𝐼𝑍𝐸 − 1 ⇒ 𝑎 𝑗 ≤ 𝑎[𝑗 + 1] • Функция возвращает всегда положительное значение \ 𝑟𝑒𝑠𝑢𝑙𝑡 > 0 • Функция может менять порядок элементов в массиве, но не его содержимое 0_0 • Если в дереве присутствует искомый элемент, то функциях его обязательно найдёт O_O • Программа не держит в памяти секретные данные дольше, чем это требуется для их обработки @_@ • … Примеры требований по функциональности -
Заголовок • CompCert – компилятор языка Clight (Coq > Ocaml)
• seL4 – микроядро L4 (Cparse > Isabelle/HOL) • CertiKOS – Certified Kit Operating System • Ironclad – End-to-End Security via Automated Full- System Verification (Dafny) • FSCQ – A Formally Certified Crash-proof File System (Coq) • Quark – веб-браузер с верифицированным ядром (Coq) Известные проекты -
Заголовок Высшая школа экономики, Москва, 2016 Что можно сказать о
функции на языке Си по её коду? (1) • Она существует и написана на языке Си; -
Заголовок Высшая школа экономики, Москва, 2016 Что можно сказать о
функции на языке Си по её коду? (1) • Она существует и написана на языке Си; • Это чистая функция; • Она вычисляет среднее между двумя целыми числами; -
Заголовок Высшая школа экономики, Москва, 2016 Что можно сказать о
функции на языке Си по её коду? (1) • Она существует и написана на языке Си; • Это чистая функция; • Она вычисляет среднее между двумя целыми числами; • При определённых условиях возможно целочисленное переполнение. -
Заголовок Высшая школа экономики, Москва, 2016 Что можно сказать о
функции на языке Си по её коду? (2) • Возможно ли целочисленное переполнение в том контексте, где функция вызывается? • Считать ли возможное целочисленное переполнение ошибкой? -
Заголовок Высшая школа экономики, Москва, 2016 Что можно сказать о
функции на языке Си по её коду? (3) • Контекст: функция двоичного поиска; • Индексы l и h неотрицательны, l не превосходит h; • Возможна ошибка выхода за границу массива при целочисленном переполнении. -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (1) •Описать контекст вызова: 𝜙: 𝑍 × 𝑍 → ⊤, ⊥ 𝜙 𝑎, 𝑏 ≡ 𝑎 ≥ 0 ∧ 𝑏 ≥ 0 ∧ 𝑎 ≤ 𝑏 -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (1) •Описать контекст вызова: 𝜙: 𝑍 × 𝑍 → ⊤, ⊥ 𝜙 𝑎, 𝑏 ≡ 𝑎 ≥ 0 ∧ 𝑏 ≥ 0 ∧ 𝑎 ≤ 𝑏 •Описать требования, которым должны удовлетворять результаты: 𝜓: 𝑍 × 𝑍 × 𝑍 → {⊤, ⊥} 𝜓 𝑎, 𝑏, 𝑟𝑒𝑠𝑢𝑙𝑡 ≡ 𝑟𝑒𝑠𝑢𝑙𝑡 = 𝑎 + 𝑏 2 -
Заголовок Как доказать что код функции корректен? (2) • Формализовать
понятие ошибки (целочисленное переполнение): 𝑖𝑛_𝑏𝑜𝑢𝑛𝑑𝑠: 𝑍 → {⊤, ⊥} 𝑖𝑛_𝑏𝑜𝑢𝑛𝑑𝑠 𝑛 ≡ 𝑀𝐼𝑁_𝐼𝑁𝑇 ≤ 𝑛 ≤ 𝑀𝐴𝑋_𝐼𝑁𝑇 -
Заголовок Как доказать что код функции корректен? (2) • Формализовать
понятие ошибки (целочисленное переполнение): 𝑖𝑛_𝑏𝑜𝑢𝑛𝑑𝑠: 𝑍 → {⊤, ⊥} 𝑖𝑛_𝑏𝑜𝑢𝑛𝑑𝑠 𝑛 ≡ 𝑀𝐼𝑁_𝐼𝑁𝑇 ≤ 𝑛 ≤ 𝑀𝐴𝑋_𝐼𝑁𝑇 • Формализовать код программы: функция 𝑀 𝑎𝑣𝑟 , которая возвращает результат 𝑀 𝑎𝑣𝑟 (𝑎, 𝑏) в соответствии со своим программным кодом если завершается и завершается без ошибки, иначе возвращается специальное значение 𝜔 -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (2) • Формализовать понятие ошибки (целочисленное переполнение): 𝑖𝑛_𝑏𝑜𝑢𝑛𝑑𝑠: 𝑍 → {⊤, ⊥} 𝑖𝑛_𝑏𝑜𝑢𝑛𝑑𝑠 𝑛 ≡ 𝑀𝐼𝑁_𝐼𝑁𝑇 ≤ 𝑛 ≤ 𝑀𝐴𝑋_𝐼𝑁𝑇 • Формализовать код программы: функция 𝑀 𝑎𝑣𝑟 , которая возвращает результат 𝑀 𝑎𝑣𝑟 (𝑎, 𝑏) в соответствии со своим программным кодом если завершается и завершается без ошибки, иначе возвращается специальное значение 𝜔 • Доказать полную корректность: ∀𝑎, 𝑏 𝜙 𝑎, 𝑏 ⇒ 𝑀 𝑎𝑣𝑟 𝑎, 𝑏 ≠ 𝜔 && 𝜓 𝑎, 𝑏, 𝑀 𝑎𝑣𝑟 𝑎, 𝑏 -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (3) function to_int bint : int function of_int int : bint -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (3) function to_int bint : int function of_int int : bint predicate in_bounds (n:int) = -2147483648 <= n && n <= 2147483647 -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (3) function to_int bint : int function of_int int : bint predicate in_bounds (n:int) = -2147483648 <= n && n <= 2147483647 constant a, b, o1, o2: bint axiom H0: a >= of_int 0 && b >= of_int 0 && b >= a -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (3) function to_int bint : int function of_int int : bint predicate in_bounds (n:int) = -2147483648 <= n && n <= 2147483647 constant a, b, o1, o2: bint axiom H0: a >= of_int 0 && b >= of_int 0 && b >= a axiom H1: to_int o1 = 2 axiom H2: to_int o2 = (to_int a + to_int b) -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (3) function to_int bint : int function of_int int : bint predicate in_bounds (n:int) = -2147483648 <= n && n <= 2147483647 constant a, b, o1, o2: bint axiom H0: a >= of_int 0 && b >= of_int 0 && b >= a axiom H1: to_int o1 = 2 axiom H2: to_int o2 = (to_int a + to_int b) goal avr_safety: in_bounds 2 -> -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (3) function to_int bint : int function of_int int : bint predicate in_bounds (n:int) = -2147483648 <= n && n <= 2147483647 constant a, b, o1, o2: bint axiom H0: a >= of_int 0 && b >= of_int 0 && b >= a axiom H1: to_int o1 = 2 axiom H2: to_int o2 = (to_int a + to_int b) goal avr_safety: in_bounds 2 -> in_bounds(to_int a + to_int b) -> -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (3) function to_int bint : int function of_int int : bint predicate in_bounds (n:int) = -2147483648 <= n && n <= 2147483647 constant a, b, o1, o2: bint axiom H0: a >= of_int 0 && b >= of_int 0 && b >= a axiom H1: to_int o1 = 2 axiom H2: to_int o2 = (to_int a + to_int b) goal avr_safety: in_bounds 2 -> in_bounds(to_int a + to_int b) -> not to_int o1 = 0 -> -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (3) function to_int bint : int function of_int int : bint predicate in_bounds (n:int) = -2147483648 <= n && n <= 2147483647 constant a, b, o1, o2: bint axiom H0: a >= of_int 0 && b >= of_int 0 && b >= a axiom H1: to_int o1 = 2 axiom H2: to_int o2 = (to_int a + to_int b) goal avr_safety: in_bounds 2 -> in_bounds(to_int a + to_int b) -> not to_int o1 = 0 -> in_bounds(div (to_int o2) (to_int o1)) -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (4) -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (4) Условие верификации -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (4) Условие верификации Исправление кода -
Заголовок Высшая школа экономики, Москва, 2016 Как доказать что код
функции корректен? (4) Условие верификации Исправление кода Уточнение спецификаций -
Стек инструментов дедуктивной верификации CIL CIL with annotations С program
with ACSL annotations Frama-C -
Высшая школа экономики, Москва, 2016 Стек инструментов дедуктивной верификации CIL
CIL with annotations С program with ACSL annotations Jessie program (with annotations built-in) Jessie translator Why3 support Jessie2 CIL visitors (rewriters) Frama-C Jessie plugin -
Высшая школа экономики, Москва, 2016 Стек инструментов дедуктивной верификации CIL
CIL with annotations С program with ACSL annotations Jessie program (with annotations built-in) Jessie translator Why3 support Why3 VC generator Why3 WhyML modules Verification conditions in Why3ML VC transformations Why3 encoders + drivers Logical formulas/scripts in SMT-LIB/SMT-LIBv2/native format Coq, PVS, Isabelle proof templates Why3 transformation/proof/shapes database Alt-Ergo Z3 CVC4 Coq PVS Why3 IDE ... Jessie2 CIL visitors (rewriters) Frama-C Jessie plugin Why3 Isabelle ... -
Заголовок • Код • Что формальная верификация может проверить и
чего не может? • Формальные спецификации • Можно ли разработать формальные спецификации до стадии написания кода? • Что в них должно быть отображено? • Что инструмент (его модели и теоретическая основа) позволяет в них отобразить? • Насколько полными/точными/непротиворечивыми должны быть спецификации? • Код и спецификации • Можно ли дважды ошибиться и при этом доказать, что всё корректно? Где может встречаться ошибка? -
Заголовок • Зависит от того, какие модели заложены в инструментах
верификации • Памяти, целочисленной арифметики, битовой арифметики… • Чем сложнее модель, тем детальнее она отражает действительность • Чем сложнее модель, тем более сложными становятся формулы условий верификации • Чем сложнее формулы, тем хуже на них работают автоматические доказатели логических формул • Аналогия QEMU⟺BOCHS Ошибки, которые «ловятся» в коде (1) -
Заголовок • Деление на ноль • Разыменование указателя • Некратный
сдвиг типизированного указателя • Выход за границу массива • Целочисленное переполнение • Переполнение при операциях с плавающей запятой • Бесконечные циклы • … Ошибки, которые «ловятся» в коде (2) -
Заголовок •А как вы моделируете память (read, write)? char *p
= "побольше цинизма, Киса"; p[0] = 'П'; •А как вы моделируете указатели (переполнение указателей)? char *p = UINT_MAX - 1; strlen(p); Вопрос гарантии отсутствия ошибок в коде (ошибки, которые «не ловятся») -
Заголовок •А как вы моделируете стек (ограниченный или безграничный)? #define
STACK_SIZE 1000*0x1000 //@ ensures \result == 1; int main(void) { int a[STACK_SIZE]; memset(a, 0, STACK_SIZE); a[STACK_SIZE-1] = 1; return a[STACK_SIZE-1]; } Вопрос гарантии отсутствия ошибок в коде (ошибки, которые «не ловятся») -
Заголовок •Что мы пишем в функциональные требования? ensures \result >=
0; long abs(int a) { return 4; } Ошибки, специфичные для спецификаций (полнота) (1) -
Заголовок •Что мы пишем в функциональные требования? ensures \result >=
0; long abs(int a) { return 4; } •Как мы пишем функциональные требования? unsigned abs(int a) return a >= 0 ? a :-((long)a); ensures \result == a || \result == -a; Ошибки, специфичные для спецификаций (полнота) (1) -
Заголовок •Что мы пишем в функциональные требования? ensures \result >=
0; long abs(int a) { return 4; } •Как мы пишем функциональные требования? unsigned abs(int a) return a >= 0 ? a :-((long)a); ensures \result == a || \result == -a; ensures \result == -a <==> a < 0; Ошибки, специфичные для спецификаций (полнота) (1) -
Заголовок •Что мы пишем в функциональные требования? ensures \result >=
0; long abs(int a) { return 4; } •Как мы пишем функциональные требования? unsigned abs(int a) return a >= 0 ? a :-((long)a); ensures \result == a || \result == -a; ensures \result == -a <==> a < 0; ensures a>=0 ? \result==a : \result==-a; Ошибки, специфичные для спецификаций (полнота) (1) -
Заголовок • Какие свойства мы выражаем в требованиях? requires n
== 2 && \valid(a+(0..n-1)); ensures \forall integer i, j; 0 <= i < j < n ==> a[i] <= a[j]; // отсортированность void sort(size_t n, int a[n]) { a[0] = 1; a[1] = 2; } Ошибки, специфичные для спецификаций (полнота) (2) -
Заголовок • Какие свойства мы выражаем в требованиях? requires n
== 2 && \valid(a+(0..n-1)); ensures \forall integer i, j; 0 <= i < j < n ==> a[i] <= a[j]; // отсортированность void sort(size_t n, int a[n]) { a[0] = 1; a[1] = 2; } • Как правильно их выразить? ... //сохранение всех элементов ensures \forall int *i; a <= i < a + n ==> Сount{Pre}(a, n, *i) == Сount{Post}(a, n, *i); void sort(size_t n,int a[n]){if(a[0]>a[1])swap(a,0,1);} Ошибки, специфичные для спецификаций (полнота) (2) -
Заголовок • Противоречие в логических утверждениях a == 1 &&
a == 2 Ошибки, специфичные для спецификаций (противоречия) (1) -
Заголовок • Противоречие в логических утверждениях a == 1 &&
a == 2 • Изо лжи следует всё, что угодно requires 0 == 1; ensures \result == 0 && \result == 1 && \result == 2; int main(void) { int a = 1; return a / 0; } Ошибки, специфичные для спецификаций (противоречия) (1) -
Заголовок • Противоречие в логических утверждениях a == 1 &&
a == 2 • Изо лжи следует всё, что угодно requires 0 == 1; ensures \result == 0 && \result == 1 && \result == 2; int main(void) { int a = 1; return a / 0; } • Мертвый код void test(int a){ if (a > 0) if (a < 0) a/0; } Ошибки, специфичные для спецификаций (противоречия) (1) -
Заголовок • Verification Engineering of Safety and Security Critical Industrial
Applications (VESSEDIA) • STANCE project • Programme Inter Carnot Fraunhofer from BMBF and ANR • Начало проекта - 2009 Пример ошибки в реальном проекте (1) -
Заголовок logic integer Count{L}(int *a, integer m, integer n, int
v); Пример ошибки в реальном проекте (2) -
Заголовок logic integer Count{L}(int *a, integer m, integer n, int
v); axiom CountSectionEmpty: \forall int *a, v, integer m, n; n <= m ==> Count(a, m, n, v) == 0; Пример ошибки в реальном проекте (2) -
Заголовок logic integer Count{L}(int *a, integer m, integer n, int
v); axiom CountSectionEmpty: \forall int *a, v, integer m, n; n <= m ==> Count(a, m, n, v) == 0; axiom CountSectionHit: \forall int *a, v, integer n, m; a[n] == v ==> Count(a,m,n+1,v)==Count(a,m,n,v)+1; Пример ошибки в реальном проекте (2) -
Заголовок logic integer Count{L}(int *a, integer m, integer n, int
v); axiom CountSectionEmpty: \forall int *a, v, integer m, n; n <= m ==> Count(a, m, n, v) == 0; axiom CountSectionHit: \forall int *a, v, integer n, m; a[n] == v ==> Count(a,m,n+1,v)==Count(a,m,n,v)+1; int a = 5; assert Count(&a+1,0,-1,5) == 0 && Count(&a+1,0,0,5) == 0; Пример ошибки в реальном проекте (2) -
Заголовок logic integer Count{L}(int *a, integer m, integer n, int
v); axiom CountSectionEmpty: \forall int *a, v, integer m, n; n <= m ==> Count(a, m, n, v) == 0; axiom CountSectionHit: \forall int *a, v, integer n, m; a[n] == v ==> Count(a,m,n+1,v)==Count(a,m,n,v)+1; int a = 5; assert Count(&a+1,0,-1,5) == 0 && Count(&a+1,0,0,5) == 0; assert Count(&a+1,0,0,5) == Count(&a + 1,0,-1,5)+1; Пример ошибки в реальном проекте (2) -
Заголовок logic integer Count{L}(int *a, integer m, integer n, int
v); axiom CountSectionEmpty: \forall int *a, v, integer m, n; n <= m ==> Count(a, m, n, v) == 0; axiom CountSectionHit: \forall int *a, v, integer n, m; a[n] == v ==> Count(a,m,n+1,v)==Count(a,m,n,v)+1; int a = 5; assert Count(&a+1,0,-1,5) == 0 && Count(&a+1,0,0,5) == 0; assert Count(&a+1,0,0,5) == Count(&a + 1,0,-1,5)+1; assert 0 == 1; Пример ошибки в реальном проекте (2) -
Заголовок size_t strlen(const char *s) { const char *sc; for
(sc = s; *sc != '\0'; ++sc) /* nothing */; return sc - s; } Как выглядит разработка спецификации для функции? (1) -
Заголовок requires \exists size_t i; 0 <= i && s[i]
== '\0' && \valid(s+(0..i)); size_t strlen(const char *s) Как выглядит разработка спецификации для функции? (2) (Контракт) -
Заголовок requires \exists size_t i; 0 <= i && s[i]
== '\0' && \valid(s+(0..i)); assigns \nothing; size_t strlen(const char *s) Как выглядит разработка спецификации для функции? (2) (Контракт) -
Заголовок requires \exists size_t i; 0 <= i && s[i]
== '\0' && \valid(s+(0..i)); assigns \nothing; ensures s[\result] == '\0'; size_t strlen(const char *s) Как выглядит разработка спецификации для функции? (2) (Контракт) -
Заголовок requires \exists size_t i; 0 <= i && s[i]
== '\0' && \valid(s+(0..i)); assigns \nothing; ensures s[\result] == '\0'; ensures \forall size_t i; 0 <= i< \result ==> s[i] != '\0'; size_t strlen(const char *s) Как выглядит разработка спецификации для функции? (2) (Контракт) -
Заголовок const char *sc; /*@ loop invariant s <= sc;
*/ for (sc = s; *sc != '\0'; ++sc) /* nothing */; return sc - s; Как выглядит разработка спецификации для функции? (3) (Инварианты цикла) -
Заголовок const char *sc; /*@ loop invariant s <= sc;
loop invariant \forall char *p; s <= p < sc ==> *p != '\0'; */ for (sc = s; *sc != '\0'; ++sc) /* nothing */; return sc - s; Как выглядит разработка спецификации для функции? (3) (Инварианты цикла) -
Заголовок const char *sc; /*@ loop invariant s <= sc;
loop invariant \forall char *p; s <= p < sc ==> *p != '\0'; loop variant SIZE_MAX - (sc - s); */ for (sc = s; *sc != '\0'; ++sc) /* nothing */; return sc - s; Как выглядит разработка спецификации для функции? (3) (Инварианты цикла) -
Заголовок logic integer strlen(char *s) = s == '\0' ?
0 : 1 + strlen(s + 1); Как выглядит разработка спецификации для функции? (7) (аксиоматика) -
Заголовок logic integer strlen(char *s) = s == '\0' ?
0 : 1 + strlen(s + 1); lemma strlen_shift: \forall char *s; (\exists integer i; 0 <= i && s[i] == '\0') && *s != '\0' ==> strlen(s) == strlen(s + 1) + 1; Как выглядит разработка спецификации для функции? (7) (аксиоматика) -
Заголовок logic integer strlen(char *s) = s == '\0' ?
0 : 1 + strlen(s + 1); lemma strlen_shift: \forall char *s; (\exists integer i; 0 <= i && s[i] == '\0') && *s != '\0' ==> strlen(s) == strlen(s + 1) + 1; lemma strlen_strend: \forall char *s; *s == '\0' ==> strlen(s) == 0; Как выглядит разработка спецификации для функции? (7) (аксиоматика) -
Заголовок /*@ ... ensures \result == strlen(s); */ size_t strlen_str(const
char *s) { const char *sc; /*@ ... loop invariant strlen(s) == strlen(sc) + (sc - s); ... */ for (sc = s; *sc != '\0'; ++sc) /* nothing */; return sc - s; } Как выглядит разработка спецификации для функции? (8) (аксиоматика) -
Заголовок • Трудоёмкость • В разы больше чем разработка •
Каждой строчке кода соответствует ~3-5 строчек спецификаций • Инструменты поддерживают не все конструкции языков программирования • Goto назад по коду • Switch с “проваливающимися” case • Цикломатическая сложность функций (< 15) • … • Применяется для проектов небольшого размера • обычно не более 10 тыс. строк Ограничения по применению дедуктивной верификации -
Заголовок • Серебряной пули не существует • Формальная верификация имеет
как плюсы, так и минусы • Сложность применения • Что мы доказываем (безопасность(safety), функциональные требования) • Предположения, исходя из которых проводится верификация • Формальная верификация не гарантирует отсутствие всех ошибок • Формальная верификация не является заменой тестированию • При дедуктивной верификации существенна роль человека Резюме -
Заголовок • Система верификации на основе Frama- C+Jessie+Why3 опубликована под
свободной лицензией • http://linuxtesting.ru/astraver • Руководства и введение в инструменты на русском • http://astraver.linuxtesting.org/ • Спецификации для библиотечных функций ядра Linux • https://github.com/evdenis/verker/ Дополнительная информация