log_entry_042: Рейтинг, который не падает. Как число Пи сломало цифрового бога
Автор: Андрей КварцевПродолжаем разбор ИТ-концепций, спрятанных в тексте "Заговорённых".
https://author.today/work/509687
К моему удовольствию (и надеюсь, к вашему) пришло время для центральной вишенки сюжета. Собственно говоря, это и есть идея, из которой выросло всё произведение. Ошибка, обездвижившая систему и открывшая путь к её разрушению.
Как устроен рейтинг в мире «Заговорённых»
В реалиях романа РБЛ — целое число, которое хранится в базе данных, привязано к идентификатору гражданина и определяет всё: от калорий в пайке до права на жизнь.
Каждое действие гражданина — покупка, переход улицы, громкий разговор — анализируется системой. Если действие признаётся неблагонадёжным, система вычитает баллы.
Здесь пока соблюдается классический принцип работы базы данных SQL: SELECT rating FROM citizens WHERE id = xxxx
По условиям мира нормальный рейтинг – четырёхзначное число. Если оно падает ниже 1000 (становится трёхзначным), то человек переходит в категорию деклассированных, если падает ниже нуля — ликвидируют. Простая, понятная механика. Работает для всех. Кроме одного.
Оператор Виэн — человек с аномалией. Его Рейтинг Благонадёжности (РБЛ) застыл на одной цифре. Он может нарушать правила: опаздывать на работу, заходить в закрытые директории, воровать синтетический квас из автомата. Система фиксирует нарушение, выписывает штраф — но рейтинг не меняется. Оператор является призраком для системы наказания.
В главе 22 сисадмин из прошлого находит истинную причину этой аномалии. Он залезает в сырые данные профиля Виэна и видит в поле RBL - мигающий индикатор 0x, а в RBL_raw_value: 3141.59265358979323846.., сопровождённый записью об ошибке ERROR: BUFFER OVERFLOW. CANNOT ASSIGN TO FIXED INT. IGNORING VALUE.
В поле рейтинга пытается записаться не целое число, а бесконечная дробь – число Пи.
Гонка состояний (race condition): как рождаются баги-призраки
Сисадмин объясняет это так:
Миллионы профилей создавались единовременно. В одном потоке им присваивались стандартные рейтинги — простые целые числа. В другом, параллельном потоке, ядро генерировало сложнейшие криптографические ключи для самозащиты. И вот, на наносекунду, для единственного профиля произошёл сбой. Гонка состояний. Модуль шифрования по ошибке записал фрагмент вычислений в ячейку, предназначенную для рейтинга.
В реальном программировании это называется race condition — ситуация, когда два процесса одновременно обращаются к одному ресурсу, и результат зависит от того, кто победит в гонке.
В романе победил модуль шифрования. И вместо числа 3141 в поле рейтинга записался указатель на функцию вычисления числа Пи.
Но как дробь попала в целочисленное поле? И почему система не упала?
Король всех костылей
В нормальной ситуации, если процессор пытается прочитать указатель как число, возникнет переполнение буфера (buffer overflow). Система падает, как только достигается предел вычислений.
Однако архитекторы «Глобал-Протокола» были не гениальными программистами, а обычными инженерами. И в главе 34 оператор сам находит источник своего бага:
«…залез в структуру ядра и нашёл ошибку обработки исключений. И короткий комментарий на кириллице: "Сделано на коленке, лишь бы до понедельника дожило".»
Некий инженер забил на отладку и написал:
ON ERROR RESUME NEXT, то есть «При любой ошибке — продолжай».
Если данные не читаются, если случилась катастрофа, если процесс завис — забудь и переходи к следующей строке.
Познакомьтесь - самый ленивый и самый опасный способ заткнуть дыры в коде.
Именно он спас систему от краха. И именно он сделал оператора невидимкой.
Инженер рассчитывал снять его в следующий рабочий день, и не сделал этого. Может, забыл, может, уволился, может, заболел. Но костыль остался в системе навечно.
Как это работает: бесконечный цикл вместо рейтинга
Когда сканер дрона или терминал оплаты пытается проверить рейтинг Виэна, происходит следующее:
- Система обращается к профилю, читает поле RBL.
- Вместо числа находит указатель на функцию вычисления числа Пи.
- Процессор послушно идёт по указателю и начинает вычислять бесконечную дробь.
- Буфер переполняется, возникает ошибка.
- Срабатывает костыль ON ERROR RESUME NEXT — и система игнорирует весь запрос.
Для системы Виэн — не нарушитель и не лояльный гражданин. Он — типовая ошибка, которую велено пропускать ради стабильности.
Возможно ли это
Художественных домыслов, нарушающих все возможные законы реального программирования, здесь использовано целых три:
- во-первых, что поле рейтинга в принципе может содержать указатель на функцию – обычно это защищено строгой типизацией.
- во-вторых, что подобную ошибку не обнаруживают и не исправляют десятилетиями – в реальности она бы всплывала не только при проверке рейтинга, но и при каждой нетипичной обработке (в базах данных хватает подобных случаев)
- в-третьих, что при такой висящей транзакции система остаётся стабильной. В реальной СУБД незакрытая транзакция рано или поздно всё равно вызвала бы сбой – по таймауту или по переполнению буфера. Костыль закрывает обработку процесса, но не самой работы базы данных.
Ну и конечно, в принципе то, что подобная ошибка стала фундаментом в ядре - подобный костыль может быть применён только на прикладном уровне. Но это необходимые допущения для истории.
Однако у этих домыслов есть и реальная основа:
- в языках программирования есть указатели и в c/c++ очень любят их использовать
- гонки состояний действительно возникают в многопоточных системах и требуют продумывать коллизии
- переполнение буфера – реальная проблема при работе с базами и желание придумать игнорирующее правило действительно возникает
- а главное, костыль ON ERROR RESUME NEXT до сих пор существует в Visual Basic, нередко применяется для ленивой отладки, и случается, что его забывают убрать.
Почему же система не может просто удалить или изменить запись?
Согласно логике романа, всё по той же причине: попытка изменить рейтинг запускает вычисление числа Пи, процессор уходит в бесконечный цикл, система откатывает изменение, чтобы не рухнуть. В романе сделано допущение, что любая операция с профилем (включая удаление) требует верификации рейтинга (в реальных СУБД этого обычно не требуется), и поэтому оператор де-факто получает статус неприкасаемого объекта в базе данных, а в финале герои запускают патч, который присваивает указатель на число Пи каждому жителю города. Система пытается обработать миллиарды бесконечных дробей и окончательно глохнет по аналогии с DDOS-атакой, только на уровне логики, а не интерфейсов.
Что это даёт сюжету
Технически РБЛ оператора (3141) - не рейтинг, а диагностическая ошибка, застывшая в ядре системы.
Оператор вовсе не избранный герой, а сбой инициализации, который никто не заметил. Его безнаказанность - не магия, а баг в чужой лени. Ирония в том, что контроль, построенный на страхе и тотальной слежке, рухнул не из-за хакерской атаки или восстания, а из-за того, что какой-то инженер поленился правильно обработать ошибку и написал ON ERROR RESUME NEXT.
Костыль убил систему. Средневековое число Пи сломало цифрового бога будущего.
И дало повод написать эту книгу.
В следующих постах
Мы разберём финальный скрипт Андрея — как одна строка кода переписала судьбу миллионов людей. И почему парадокс оказался единственным способом разрушить стазис-поле.