Баг, который прятался 18 лет: как OpenAI нашла ошибку

Инженеры OpenAI нашли баг, который жил в библиотеке libunwind 18 лет. Как искали и нашли — читайте в обзоре.

Инженеры OpenAI рассказали историю, которая звучит как детектив, но с ассемблером и миллионами запросов.

Они нашли баг, который жил в популярной библиотеке libunwind целых 18 лет. Он срабатывал только в микроскопическом окне — буквально одна процессорная инструкция, около 100 пикосекунд (это 0,0000000001 секунды).

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


С чего всё началось

OpenAI использует для поиска по перепискам в ChatGPT инфраструктуру Rockset — C++-поисковый движок, который купили в 2024 году.

И там начали падать процессы.

Причём странно:

  • функция возвращалась не туда — иногда по нулевому адресу (NULL)
  • иногда указатель стека оказывался смещён на 8 байт
  • никаких явных причин не было

Инженеры перебирали гипотезы — и каждая разбивалась о факты. Баг выглядел невозможным.


Как меняли подход

Сначала отлаживали как врач: брали один «труп» (core-дамп) и пытались поставить диагноз по деталям. Это не работало.

Тогда поменяли подход на эпидемиологический — перестали смотреть на единичный случай и посмотрели на всю «популяцию» крашей.

Скрипт для анализа написал… сам ChatGPT. Он скачал и разметил все дампы памяти Rockset за год.

И корреляции проявились мгновенно.


Два бага вместо одного

На чистых данных оказалось, что проблема — не одна, а две независимые:

  1. Тихий аппаратный сбой. Процессор на одном хосте Azure просто неправильно считал.
  2. Гонка потоков (race condition) в libunwind. Вот это оказалось главным.

Что за баг и как он работал

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

Если именно в этот момент прилетает системный сигнал, ядро затирает эту память — и адрес возврата превращается в NULL. Программа падает.


Почему он не вылезал 18 лет

Код появился ещё в 2007–2008 годах в первой версии libunwind для x86_64. И всё это время он спокойно работал.

Читать ещё  Сайт рухнул из-за DDoS-атак, а хостинг не при чем?

Почему же он проявился у OpenAI?

  • Огромная нагрузка. Rockset кидает очень много исключений.
  • Частые сигналы. Очень часто шлёт системные сигналы.
  • Недавнее изменение. Обработчик сигнала стал занимать больше стека.

Только на пересечении этих трёх факторов баг пересёк порог видимости.


Как починили

В качестве лечения OpenAI:

  • Пересела с GNU libunwind на механизм раскрутки из libgcc
  • Отправила в libunwind воспроизводимый пример и код исправления

Что это значит и какой вывод

Главный вывод инженеры OpenAI делают не про ассемблер и не про сигналы. А про подход к отладке.

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


Если коротко

OpenAI нашла баг, который 18 лет жил в библиотеке для раскрутки стека. Он проявлялся только на масштабах ChatGPT — в окне шириной в одну инструкцию (100 пикосекунд).

История показывает, что:

  1. «Невозможные» баги часто оказываются реальными. Просто они проявляются только на очень высоких нагрузках.
  2. Качественные данные важнее гениальных догадок. Когда смешаны два разных явления — ничего не понятно. Когда данные разделены — всё становится очевидным.
  3. Даже старый, проверенный код может содержать ошибки. Баг был написан 18 лет назад и всё это время просто… ждал.

И да, для анализа использовали самого ChatGPT. Ирония судьбы — ИИ помог найти баг, который мешал работать ИИ.

Читайте также: Новости кибербезопасности

 

Селектел-Защита от DDoS‑атак

Источник
habr.com

Добавить комментарий

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

Кнопка «Наверх»