← На главную

Anubis: недетерминированная сборка упёрлась в баг LLVM

18.06.2026 05:10 · hackernews

Автор Anubis решил добавить на сайт WebAssembly-проверку вместо SHA256, чтобы администраторы могли защищать ресурсы нестандартным proof of work. Логика проверки определяется в одном месте и клиент, и сервер исполняют один и тот же WebAssembly-код. Но встал вопрос: что делать, если у пользователя WebAssembly отключён? Выгонять людей нельзя. Тогда автор вспомнил доклад «The Birth and Death of JavaScript» и просто скомпилировал WebAssembly обратно в JavaScript через утилиту wasm2js из проекта binaryen.

Беда в том, что дистрибутивы Linux таскают древние версии wasm2js, и их результат не совпадает с версией из Homebrew. Для детерминированной сборки пришлось зашить wasm2js в репозиторий — автор собрал его под WebAssembly с помощью wasi-sdk. И тут начался ад воспроизводимости.

Сначала — тривиальное: C++-макросы __DATE__ и __TIME__. Одна и та же программа выдаёт разные бинарники каждую секунду. Потом выяснилось, что clang при линковке молча вызывает wasm-opt из $PATH. На DGX Spark с архитектурой arm64 стояла версия 108, которая не умела обрабатывать WebAssembly Exceptions — сборка падала. Решение — флаг --no-wasm-opt.

Дальше — хуже. Clang генерирует адресно-зависимый код: порядок блоков try_table зависит от того, в каком порядке обходятся указатели в исключениях. На arm64 и x86_64 порядок получается разный, и бинарники различаются на ~29 байт. Автор отключил рандомизацию адресов через setarch --addr-no-randomize и зафиксировал контрольные суммы sha256 для обеих архитектур в CI. Теперь сборка детерминирована внутри каждой архитектуры, но кроссплатформенная воспроизводимость упирается в баг LLVM — автор просит разработчиков LLVM добавить seed для фиксации итерации. Пока так.

Читать оригинал →