Показаны сообщения с ярлыком Идеология. Показать все сообщения
Показаны сообщения с ярлыком Идеология. Показать все сообщения

среда, 31 июля 2024 г.

Свойства. Доказуемый

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

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

среда, 5 июня 2024 г.

Ошибочное состояние

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

Ближайшим аналогом из других языков является термин «неопределённое поведение»[0] с поправкой на правильно выставленный приоритет в отношении понимания и обработки.

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

Ключевая особенность ошибочного состояния заключается в том, что любое воплощение поведения исполнителя кода при достижении или предсказании этого состояния не влияет на поведение правильного кода, то есть соблюдающего правила языка. При адекватно заданных правилах наличие понятия ошибочного состояния в языке не приводит к появлению дополнительных ошибок в коде, как часто ошибочно считается. Наоборот — ошибочное состояние возникает из-за логических ошибок в коде, и поэтому наличие этого понятия важно для возможности обнаружения и устранения ошибок. И хотя возможно такое определение языка, при котором программа никогда не сможет выйти за пределы применимости его правил, такой язык не приведёт к устранению логических ошибок программы, а наоборот, усложнит их выявление и предотвращение. Нельзя автоматически выявлять ошибку в том поведении, на которое может опираться разработчик, вписывая его в основную логику. В общем случае исполнитель языка не сможет отличить намеренную логику от случайной ошибки.

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

  • Обращение к массиву по индексу, выходящему за его пределы
  • Обращение по пустому указателю
  • Обращение по недопустимому адресу в низкоуровневых операциях с данными
  • Арифметическое переполнение допустимого диапазона чисел — и целых, и дробей
  • Деление на 0
  • Чтение значения переменной с ошибочным значением (не- или де-начализированной)
  • Преобразование типа для недопустимого значения
  • Получение ложного значения в утверждении
  • Возможность достижения потоком выполнения места, помеченного как недостижимое
  • Фактическое зацикливание или превышение ограничения количества вычислений
  • Неявные константные выражения
  • Бесполезные выражения и инструкции, не документирующие и не оказывающие влияния на состояние

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

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

Примеры возможного воплощения поведения при ошибочном состоянии:

  • Статическое выявление как ошибки трансляции для простых случаев или при продвинутом анализе. В отличий от опциональных предупреждений, которые могут быть ложно-положительными, выявление ошибки допустимо только для достоверных нарушений правил языка с учётом контекста применения. Поиск нарушений ведётся во все ветках кода без учёта фактической достижимости их выполнения.
  • Аварийное завершение подзадачи в итоге проверок во время исполнения программы. Подходит для отладки и для быстрых проверок хорошо отлаженного кода в выпускной версии.
  • Регистрация ошибки в системе и пометка выходного значения как ошибочного. Другие вычисления с ошибочными значением тоже порождают ошибочное значение, возможно, кроме тех, что приводят к определённому значению вне зависимости от другого значения (e * 0, e & false). Если ошибочное значение добирается до существенной развилки или вывода данных, то происходит аварийное завершение, так как дальнейший учёт ошибочности не представляется возможным. Если ошибочное значение оказывается незадействованным или уничтоженным, то программа может продолжить нормальное выполнение. Этот подход позволяет предотвратить некоторые избыточные падения, оставляя возможность исправления ошибки после получения отчёта о зарегистрированных ошибках.
  • Максимизация случайного поведения. Подходит для отладки того, что сложней диагностировать аварийной остановкой, например, работу с неначализированными переменными. Изменчивое поведение свидетельствует об ошибках и препятствует закреплению случайных характеристик текущей версии исполнителя в ошибочном коде как чего-то ожидаемого[1].
  • Максимизация детерминированности поведения, которое даёт наиболее вероятную правильность. Подходит для выпускной версии, но будет полезен только в паре с предыдущим. Если нужно выбрать что-то одно, лучше выбрать предыдущий.
  • Приостановка выполнения и ожидание ответа пользователя, который может выбрать наиболее подходящее решения на основе анализа конкретной ситуации. Подходит для длительных задач личного характера.
  • Предотвращение нарушений, но без аварийной остановки кода-нарушителя, а возвратом значения ошибки, то есть преобразования ошибки кода в специальное значение, например, ошибку ввода. Подходит для повышения устойчивости кода, менее критичного к последствиям ошибок кода, и в тех подзадачах, где допустимо некоторое значение по умолчанию.
  • Отсутствие специальной реакции, как и необходимости обеспечения детерминированности. Такая возможность — это лишь признание сложности диагностики некоторых ошибок, например, зацикливания, но без требования отказа от их диагностики. Применимо не только к простым трансляторам, но и тем, что работают в связке с системами доказательства правильности, позволяющих, как минимум, доказать отсутствие в коде возможности достижения ошибочного состояния. В таком случае самому транслятору нет необходимости в собственной диагностике ошибок, и потому может быть проще, что важно, если и правильность самого транслятора должна быть доказана.

Сноски

[0] Не исключено, что неудачно выбранное название и привело к тому, что большинство разработчиков понимают эту тему неточно в той или иной степени.
[1] Противодействие закону Хирума. Именно этот закон приводит многих разработчиков к ошибочному выводу о том, что ошибочное состояние/неопределённое поведение является причиной дополнительных ошибок, ведь у них раньше работало, что достигалось ненадёжным подходом правки кода, пока не заработает, вместо устранения всех потенциально выявляемых ошибок.

понедельник, 21 ноября 2022 г.

Ансамбль языков

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

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

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

Языки:

  1. Разъёмно-интерфейсный
    1. Описания типов
  2. Разметка данных
  3. Низкоуровневый системный
    1. Версия для генерации кода
  4. Среднеуровневый системный
  5. Высокоуровневый надёжный
  6. Спецификационный-верифицирующий
  7. Преобразующий
  8. Архитектурный
  9. Быстрокодовый(скриптовый)
    1. Для гибкой записи преимущественно поверхностного кода
    2. Интерфейсный интерактивный

Разъёмно-интерфейсный

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

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

Разметка данных

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

Низкоуровневый системный

Этот язык нужен для представления кода на уровне, близком к машинному, в то же время сохраняя важные защиты высокоуровневых языков. Язык позволяет распространять код между недоверенными системами. Может служить промежуточным представлением обычных трансляторов.

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

Среднеуровневый системный

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

Высокоуровневый надёжный

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

Спецификационный-верифицирующий

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

Преобразующий[0]

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

Быстрокодовый(скриптовый)

Язык для поверхностного кодирования, для ситуативных задач. Для них требуется лёгкость ввода, и не требуется ни высокая проработанность, ни высокая производительность. В пределе такие программы могут терять актуальность даже после единственного исполнения. Уровень критичности к ошибкам сравнительно низок. Тем не менее при аккуратном проектировании возможна куда более вменяемая логика работы, чем для многих современных скриптовых языков, например, bash.

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


Сноски:
[0] Преобразования — это часть концепции поддержки наследия без раздувания сложности

четверг, 24 декабря 2020 г.

Исполнители языка

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

Можно выделить 4-е основные свойства, задающие назначение инструмента:

  1. Определяющий
  2. Юркий
  3. Доказанный
  4. Многоцелевой

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

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

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

Многоцелевой. Это основной транслятор и он должен решать множество самых разных задач разработчика, во многом, связанных между собой:

  1. Поддержка нескольких исходных языков-спутников.
  2. Трансляция в разнообразные машинные языки и промежуточные представления.
  3. Языковые и машиноспецифичные оптимизации кода.
  4. Статический анализ для выявления ошибок и, желательно, проверка доказательства правильности.
  5. Преобразования исходного кода для перехода на новые решения.
  6. Отслеживание связей в коде.
  7. Подсчёт метрик кода.

Решение многих задач приводит к объёмности, сложности и неповоротливости такого транслятора. Из-за этого он не может быть ни быстрым, ни доказанным полностью, но может встраивать в себя остальные разновидности трансляторов, таким образом не лишая себя их сильных сторон.

воскресенье, 20 декабря 2020 г.

Свойства. Сопровождаемый

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

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

суббота, 23 сентября 2017 г.

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

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

понедельник, 11 июля 2016 г.

Архитектура языка

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

  1. Правильность исходного кода
  2. Простота использования языка
  3. Потенциальняя лёгкость воплощения транслятора
  4. Эффективность выходного кода

Учитывается, что:

  1. Язык ценен не только тем, что в нём есть, но и тем, чего в нём нет
  2. Добавить новую возможность легче, чем удалить старую
  3. И маловероятные ошибки могут становиться большими проблемами
  4. Возможность негарантированного обнаружения ошибки лучше гарантированного необнаружния

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

Конструктивные особенности:

  1. Синтаксис
  2. Разделяемость
  3. Маршрутизация разделов
  4. Объявления и области видимости
  5. Подпрограммы
  6. Разновидности состояния переменной
  7. Виды постоянных
  8. Состояние правильности
  9. Ошибочное состояние
  10. Присваивание
  11. Утверждение
  12. Целочисленные типы

Другое:

воскресенье, 10 июля 2016 г.

Свойства. Цельный

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

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

Современный разработчик имеет то преимущество перед первопроходцами, что основные потребности давно ясны и опробованы в разных вариантах. Всё что остаётся - это правильно распорядиться этими знаниями и не повторять ошибок предшественников.

четверг, 7 июля 2016 г.

Свойства. Эффективный

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

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

понедельник, 20 июня 2016 г.

Свойства. Дисциплинирующий

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

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

воскресенье, 19 июня 2016 г.

Свойства. Ошибкоустойчивый

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

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

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

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

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

суббота, 18 июня 2016 г.

Свойства. Мощный

Мощность - это то свойство языка, что позволяет писать довольно сложный код с минимальными затратами разработчика, что при соблюдении других принципов способствует улучшению обозримости и понятности алгоритмов. Для того же, чтобы правило всё же не входило в противоречие с простотой и понятностью, необходимо из всех возможных средств отобрать только самые важные. Иными словами, нужно взять те 10% возможностей, что небходимы для написания 90% кода, смирившись, если понадобится, с неудобством языка для более редких случаев.

вторник, 14 июня 2016 г.

Свойства. Однозначный

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

Исключение в виде неоднозначности поведения в ошибочном состоянии нужно не только для того, чтобы воплощение могло выбрать наилучшее поведение для реакции на ошибку. Важно и то, что любое гарантированное поведение при ошибках кода приводит к возможности вписать ошибку(в таком случае уже, скорее, «ошибку») как часть псевдо-нормального течения процесса. Язык защитного программирования не должен способствовать включению ошибок кода в дизайн кода.

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

среда, 8 июня 2016 г.

Свойства. Простой для разработчика

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

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

* не следуеть путать описание языка и стандартной библиотеки

понедельник, 6 июня 2016 г.

Свойства. Простой для инструментария

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

Быть простым для транслятора означает возможность использовать более простые подходы для трансляции, обойдясь без сторонних инструментов, вроде компиляторов грамматик. Меньшее количество кода и инструментов и их меньшая сложность — это также и меньшее количество ошибок. Это обстоятельство стоит помнить всегда, особенно во время творческих порывов.

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

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

Запутыватель логики(обфускатор) необходим как часть противодействия ряду атак. Учёт необходимости такой возможности позволит применять её более эффективно.

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

воскресенье, 5 июня 2016 г.

Свойства. Понятный

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

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

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

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

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

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

Свойства языка

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

  1. Понятный
  2. Простой для разработчика
  3. Потенциально простой для инструментария
  4. Однозначный
  5. Ошибкоустойчивый
  6. Доказуемый
  7. Дисциплинирующий
  8. Мощный
  9. Эффективный
  10. Цельный
  11. Сопровождаемый

Введение

Автоматизация на основе электроники всё глубже проникает в человеческую жизнь, занимая новые ниши и всё сильней на неё влияя: бытовая техника обзаводится прилагательным «умная», автомобили примеряют на себя автопилот, компании доставки испытывают квадрокоптеры и чуть ли не каждая вещь стремится обзавестись IP и устроить интернет вещей. Всё это замечательно и создаёт новые прекрасные возможности, но одновременно и несёт новые угрозы. Одной из причин которых являются ошибки программного обеспечения, которые и сами по себе способны нанести вред, а кроме того зачастую служат инструментом в руках злоумышленников. А уж автоматизация вредительской работы ускоряет ничуть не хуже автоматизации полезной.

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

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

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

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

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