Как ThreadSanitizer помог найти гонки данных в Firefox

Гонка данных в C++ возникает не просто тогда, когда программа иногда выдаёт неправильный результат. Несинхронизированный конфликтующий доступ может нарушать модель памяти языка и вести к неопределённому поведению. Команда Mozilla описала внедрение ThreadSanitizer для поиска таких ошибок в Firefox.

Что обнаружил инструмент

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

Как вводили проверки

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

Следует помнить, что успешный прогон ThreadSanitizer не доказывает отсутствия всех гонок — он покрывает только выполнённые при прогонах пути исполнения. В проектах со смешанным использованием Rust и C++ критичны границы между языками: безопасные абстракции Rust снижают риск, но низкоуровневый unsafe-код и неправильные порядки атомарных операций остаются источниками ошибок и требуют отдельной проверки.