Пандемия неполной обработки ошибок в OpenSSL
- title
- Пандемия неполной обработки ошибок в OpenSSL
- type
- summary
- summary
- Юлиан Андрес Клоде о распространённом антипаттерне вызова ERR_clear_error() для скрытия ошибок OpenSSL ценой сброса посторонних ошибок со стека
- tags
- security, software-quality, c
- sources
- openssl-error-handling-klode
- created
- 2026-07-21
- updated
- 2026-07-21
- lang
- ru
- translation_of
- openssl-error-handling-pandemic
- source_updated
- 2026-07-21
- translated
- 2026-09-01
- translator
- lllm/antigravity/gemini-3.7-flash-medium
Короткая и резкая заметка Юлиана Андреса Клоде (мейнтейнера APT) о том, что широко копируемая "лучшая практика" работы с OpenSSL - это системный опасный антипаттерн: расставлять вызовы ERR_clear_error() вокруг TLS-операций, чтобы скрыть неудобные ошибки.
Повод
Кто-то сообщил, что TLS в APT падает на системах с FIPS из-за ошибок MD5, и предложил обернуть TLS-операции в ERR_clear_error(). Клоде отказался, исходя из принципа: если компонент не обработал свои ошибки, это не даёт права сбрасывать вообще все ошибки где-то ещё - программа должна была либо упасть раньше, либо отбросить конкретную ошибку только после проверки, что это безопасно. Затем он выяснил, что этот обходной путь годами считался хорошей практикой: кодовые базы повсюду вызывают ERR_clear_error() перед TLS-операциями, а апстрим OpenSSL сам предлагает так делать.
Главная ошибка: у OpenSSL есть стек ошибок
Корень проблемы в том, что многие авторы не понимают: OpenSSL хранит стек ошибок, а не один слот под ошибку. Отсюда следуют два антипаттерна:
- Тотальная очистка. Вызов
ERR_clear_error()перед TLS-операцией зачищает всё, что лежит на стеке. Это молча сбрасывает посторонние ошибки, оставленные более ранним кодом, - ошибки, которые могут указывать на реальную проблему. Ошибку, создавшую запись, нужно исправлять (или обрабатывать там, где она произошла), а не выметать позже просто потому, что она мешает. - Проверить верхнюю и сбросить всё. Вызвать операцию OpenSSL, проверить верхнюю ошибку и, если она кажется "не такой уж критичной", сбросить весь стек. Тот же дефект: посторонние ошибки, лежащие глубже, молча выбрасываются.
Оба паттерна превращают состояние "где-то произошла ошибка" в "ошибок нет" - именно такой сбой подрывает доверие к критичному для безопасности коду. Это и есть error-stack-anti-pattern - сброс всего канала ошибок ради подавления одной записи.
Решение
Использовать механизм меток в OpenSSL для изоляции собственного контекста ошибок вместо глобальной очистки: ERR_set_mark() ставит метку, затем ERR_pop_to_mark() откатывает стек до неё. Это guard-обёртка вокруг группы операций OpenSSL, которая удаляет только те ошибки, которые сгенерировали вы, не трогая ничего от другого кода. Призыв Клоде к действию: провести аудит своей кодовой базы на каждый вызов ERR_clear_error() и выяснить, безопасен ли он или относится к плохим паттернам выше; а авторам OpenSSL - перестать поощрять практики, которые "в корне подрывают любое доверие к ПО".
Почему это важно не только для OpenSSL
Общий урок касается гигиены обработки ошибок на границах библиотек: когда библиотека предоставляет общий канал ошибок с состоянием, подход "очищу его, чтобы мой happy path заработал" почти всегда ошибочен, так как отбрасывает информацию, принадлежащую другому коду. Кроме того, это тонкая ошибка в C-стиле обработки ошибок, которую легко допустить: и reviewing-ai-code, и testing-heavy-no-review-workflow отмечают, что подобное трудно отловить исключительно на ревью - антипаттерн распространился именно потому, что локально выглядит корректным и проходит проверки. Сравните с более широкими вопросами корректности и цепочек поставок в supply-chain-security.