Bug 48459 - Надёжно включать оптфлаги, full relro, сделать поддержку cargo install
Summary: Надёжно включать оптфлаги, full relro, сделать поддержку cargo install
Status: NEW
Alias: None
Product: Sisyphus
Classification: Development
Component: rpm-build-rust (show other bugs)
Version: unstable
Hardware: all Linux
: P5 normal
Assignee: Arseny Maslennikov
QA Contact: qa-sisyphus
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-11-15 18:27 MSK by Arseny Maslennikov
Modified: 2025-08-15 09:10 MSK (History)
6 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Arseny Maslennikov 2023-11-15 18:27:37 MSK
Для того, чтобы упростить корректную сборку наших сторонних пакетов на Rust (это сегодня преимущественно, если не исключительно, приложения), предлагаю сделать следующее:

- передавать -Wl,-z,relro -Wl,-z,-now, или как у rustc принято это передавать, в числе rustflags;
- назначать rustflags в помещаемом в сборочную среду config.toml, чтобы cargo и rustc подхватывали его на всех стадиях работы rpmbuild;
  - да, тому же cargo install они тоже нужны, и отличие rustфлагов в %build и %install — одна из причин, почему cargo install принимает решение пересобрать все крейты;
- явно назначить CARGO_HOME для сборки (эту идею я всё-таки стащил у федоры); может быть строго полезно тем, кто гоняет rustup в хешере, в остальных случаях %_builddir/cargo-home нагляднее, чем $HOME/.cargo;
- включить в config.toml term.verbose=true.

А лучше ещё и автоматизировать cargo vendor + патч на config.toml, но не факт, что решение этой задачи уже на совести rpm-build-rust.
Comment 1 Сергей Жидких 2025-04-22 13:54:03 MSK
(Ответ для Arseny Maslennikov на комментарий #0)
> Для того, чтобы упростить корректную сборку наших сторонних пакетов на Rust
> (это сегодня преимущественно, если не исключительно, приложения), предлагаю
> сделать следующее:
> 
> - передавать -Wl,-z,relro -Wl,-z,-now, или как у rustc принято это
> передавать, в числе rustflags;
Можно поподробнее, что вы хотите включить во флаги компиляции? Wl (verbose) вывод, - понятно, я включу, а что за остальные флаги?

> передавать, в числе rustflags;
> <...>
> - назначать rustflags в помещаемом в сборочную среду config.toml, чтобы
> cargo и rustc подхватывали его на всех стадиях работы rpmbuild;
На тот случай если флагов по умолчанию недостаточно у cargo есть несколько переменных окружения по типу RUSTFLAGS (https://doc.rust-lang.org/cargo/reference/config.html), которые позволяют переопределить сборку проекта.

Насчёт определения config.toml в макросах сборки - не уверен насколько это рационально. Что если мейнтейнеру понадобится изменить его для отключения и включения каких-то флагов? - Ему придётся либо писать скрипт, который будет изменять конфиг, либо писать свой без макроса. Оба варианта не очень позитивные, если в какой-то момент времени понадобится совершенно другой подход к сборке.

> - явно назначить CARGO_HOME для сборки (эту идею я всё-таки стащил у
> федоры); может быть строго полезно тем, кто гоняет rustup в хешере,
У нас пока нет rustup-а. Использовать предсобранный rust с официального сайта я бы не рекомендовал.

> А лучше ещё и автоматизировать cargo vendor + патч на config.toml, но не
> факт, что решение этой задачи уже на совести rpm-build-rust.
В каком плане автоматизировать?
Comment 2 Arseny Maslennikov 2025-04-24 21:57:11 MSK
(In reply to Сергей Жидких from comment #1)
> (Ответ для Arseny Maslennikov на комментарий #0)
> > Для того, чтобы упростить корректную сборку наших сторонних пакетов на Rust
> > (это сегодня преимущественно, если не исключительно, приложения), предлагаю
> > сделать следующее:
> > 
> > - передавать -Wl,-z,relro -Wl,-z,-now, или как у rustc принято это
> > передавать, в числе rustflags;
> Можно поподробнее, что вы хотите включить во флаги компиляции? Wl (verbose)
> вывод, - понятно, я включу, а что за остальные флаги?
`-Wl,--verbose` не надо, это вообще о другом (см. man 1 ld).

Издавна unix cc задал свою (альтернативно одарённую, да) традицию структурировать многочисленные опции командной строки (GNU-традиции с короткими и длинными опциями ещё не было). GCC и позже Clang её, конечно, скопировали. Более того, rustc тоже такие аргументы понимает: обратная совместимость, накопленный опыт и старческие въевшиеся привычки — страшная сила.

Так как cc — программа-диспетчер, которая, чтобы сделать то, что от неё просили, запускает другие разные программы: ассемблер, возможно, линкер, ..., ей нужно уметь произвольные опции спускать произвольному подпроцессу. Для этого придумали вот что: когда пользователь указывает ключ `-Wl,a,b,c`, в командную строку линкера (если таковой нужно будет породить) попадут аргументы `a`, `b`, `c`. Оригинальное слово разбили по запятым.

`-Wl,-z,relro` — одно слово, оно приведёт к тому,
что компоновщику передадут `-z relro`.
`-Wl,-z,now` — другое слово, оно приведёт к тому,
что компоновщику передадут `-z now`.
Можно и вместе написать, `-Wl,-z,relro,-z,now`, эффект тот же.

Теперь о том, зачем они нужны.
Как мы уже отметили, обратная совместимость и накопленный опыт — страшная сила, особенно когда вы, делая операционную систему, добиваетесь, чтобы там вечно работали старые блобы (часто несвободные). По этой причине обработка ELFов нифига не secure by default, довольно легко случайно собрать бинарник, который слишком легко проэксплуатировать (в маздае ситуация ещё хуже). Об этом удивительно (по нынешним меркам) мало литературы и нагугливаемых материалов. Пишете графику какую-нибудь, дёрнули Mesa API — бам, ваша программа начала звать dlopen() на всё подряд, у неё стек молча стал исполняемым ﷐[U+1F631]﷑.

Это одна из причин, по которым вообще целесообразно упаковывать всю прикладуху подряд в согласованный репозиторий.

Так вот, когда нечто линкуют с -z now и/или -z relro, компоновщик помечает это нечто тегами для динамического компоновщика ld-linux.so.
-z now — это указать динамическому компоновщику, что, готовя программу к исполнению (в т. ч. "проводя релокации"), значения в её PLT для функций из других .so надо заполнить заранее, а не on first use.
-z relro — это указать динамическому компоновщику, что некоторые RW участки памяти после вычисления релокаций надо бы перемапить без PROT_WRITE, т. е. как read-only; писать туда будет больше не надо.

См. также:
% info libc 'Dynamic Linker Hardening'
% info ld 'Options'

> > - явно назначить CARGO_HOME для сборки (эту идею я всё-таки стащил у
> > федоры); может быть строго полезно тем, кто гоняет rustup в хешере,
> У нас пока нет rustup-а. Использовать предсобранный rust с официального
> сайта я бы не рекомендовал.
Тут согласен. :)
Comment 3 Arseny Maslennikov 2025-04-24 21:58:30 MSK
(In reply to Сергей Жидких from comment #1)
> (Ответ для Arseny Maslennikov на комментарий #0)
> > Для того, чтобы упростить корректную сборку наших сторонних пакетов на Rust
> > (это сегодня преимущественно, если не исключительно, приложения), предлагаю
> > сделать следующее:
> > 
> > - передавать -Wl,-z,relro -Wl,-z,-now, или как у rustc принято это
> > передавать, в числе rustflags;
> Можно поподробнее <...>? <...> - понятно, я включу,
А вы хотели бы rpm-build-rust подобрать? Просто спрашиваю для ясности. :)
Comment 4 Arseny Maslennikov 2025-04-24 22:13:50 MSK
(In reply to Сергей Жидких from comment #1)
> > А лучше ещё и автоматизировать cargo vendor + патч на config.toml, но не
> > факт, что решение этой задачи уже на совести rpm-build-rust.
> В каком плане автоматизировать?

попробую выражаться дипломатично :)

У процесса сборки пакета есть несколько фаз:
1) локальное размещение необходимых исходников (для воспроизводимости и вообще чтобы не пропали);
2) подготовка спека, сценария автоматической сборки, локальных модификаций;
3) upload сборочного задания на gyle, сборка до состояния tested, создание кармана;
4) commit в сизиф.

Шаги (2), (3), (4) автоматизированы и засекьюрены по возможности. Шаг (1) сегодня, кхм, полностью на совести мейнтейнера.

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

После этого момента в дост. новых системах программирования вендорить зависимости стало модно. Сложность шага (1) распухла настолько, что это теперь для cargo, npm, go-build и проч. __самый сложный__ с точки зрения верификации результата шаг упаковки, и у нас он осуществляется **полностью вручную** на сегодняшний день, в этой части легко ошибиться. Некоторые прыткие мейнтейнеры пишут себе личную автоматизацию этого дела, говорят.
Comment 5 Arseny Maslennikov 2025-04-24 22:18:37 MSK
(In reply to Arseny Maslennikov from comment #4)
> У процесса сборки пакета есть несколько фаз:
> 1) локальное размещение необходимых исходников (для воспроизводимости и
> вообще чтобы не пропали);
> 2) подготовка спека, сценария автоматической сборки, локальных модификаций;
> 3) upload сборочного задания на gyle, сборка до состояния tested, создание
> кармана;
> 4) commit в сизиф.
> 
> Шаги (2), (3), (4) <...> засекьюрены по возможности.
Иначе говоря, они снабжены кучей проверок качества на вшивости, глупости и неконсистентности; если процедура прошла успешно, то это означает, что целых классов ошибок упаковки уже не допущено. Есть опциональные проверки, которые можно одной строчкой включить, и они исключают ещё кое-что. А вот шаг (1)...
Comment 6 Yuri N. Sedunov 2025-04-24 23:00:20 MSK
Нужен еще штатный выключатель LTO, чтобы
%define optflags_lto %nil
как то превращался в
--config 'profile.release.lto=false'
для rust.
Comment 7 Yuri N. Sedunov 2025-04-24 23:18:34 MSK
(Ответ для Arseny Maslennikov на комментарий #5)
 
> А вот шаг (1)...

У меня этот шаг, например, однозначно и довольно однообразно описывается в спеках.
Каждый может повторить.
Comment 8 Сергей Жидких 2025-08-14 17:04:58 MSK
Здравствуйте, Арсений! Спасибо за развёрнутый ответ, по какой-то причине до меня не доходили уведомления.

По поводу full relro: 
Оно включено по умолчанию для всех таргетов которые это поддерживают (включая линукс) ещё с 2017 года в контексте:
https://github.com/rust-lang/rust/issues/29877
По этому поводу у rust есть две статьи:
https://doc.rust-lang.org/nightly/rustc/exploit-mitigations.html
https://doc.rust-lang.org/beta/rustc/codegen-options/index.html
В целом, я включить для подстраховки можно.

(Ответ для Arseny Maslennikov на комментарий #3)
> (In reply to Сергей Жидких from comment #1)
> > (Ответ для Arseny Maslennikov на комментарий #0)
> > > Для того, чтобы упростить корректную сборку наших сторонних пакетов на Rust
> > > (это сегодня преимущественно, если не исключительно, приложения), предлагаю
> > > сделать следующее:
> > > 
> > > - передавать -Wl,-z,relro -Wl,-z,-now, или как у rustc принято это
> > > передавать, в числе rustflags;
> > Можно поподробнее <...>? <...> - понятно, я включу,
> А вы хотели бы rpm-build-rust подобрать? Просто спрашиваю для ясности. :)
Да, я очень заинтересован в развитии поддержки rust в репозитории. Мейнтейнер rust уже разрешил мне обновлять пакет и если никаких проблем с поддержкой не возникнет, то его обязанности перейдут мне. Но произойдёт это правда нескоро.
Comment 9 Сергей Жидких 2025-08-14 17:09:51 MSK
(Ответ для Yuri N. Sedunov на комментарий #7)
> (Ответ для Arseny Maslennikov на комментарий #5)
>  
> > А вот шаг (1)...
> 
> У меня этот шаг, например, однозначно и довольно однообразно описывается в
> спеках.
> Каждый может повторить.

Для вендоринга rust зависимостей лучше использовать cargo-vendor-alt из пакета cargo-vendor-filterer. Он отсеивает большую часть ненужных файлов и зависимостей.
Comment 10 Сергей Жидких 2025-08-14 18:00:45 MSK
(Ответ для Arseny Maslennikov на комментарий #4)
> (In reply to Сергей Жидких from comment #1)
> > > А лучше ещё и автоматизировать cargo vendor + патч на config.toml, но не
> > > факт, что решение этой задачи уже на совести rpm-build-rust.
> > В каком плане автоматизировать?
> 
> попробую выражаться дипломатично :)
> 
> У процесса сборки пакета есть несколько фаз:
> 1) локальное размещение необходимых исходников (для воспроизводимости и
> вообще чтобы не пропали);
> 2) подготовка спека, сценария автоматической сборки, локальных модификаций;
> 3) upload сборочного задания на gyle, сборка до состояния tested, создание
> кармана;
> 4) commit в сизиф.
> 
> Шаги (2), (3), (4) автоматизированы и засекьюрены по возможности. Шаг (1)
> сегодня, кхм, полностью на совести мейнтейнера.
> 
> До середины десятых годов в типичном пакете ничего вендорить было не надо,
> исключения из этого правила были исключениями, и исходные материалы для
> пакетов были похожи на исходные материалы от апстримов в дост. степени,
> чтобы сходство было очевидно на глаз, не слишком вооружённый.
> 
> После этого момента в дост. новых системах программирования вендорить
> зависимости стало модно. Сложность шага (1) распухла настолько, что это
> теперь для cargo, npm, go-build и проч. __самый сложный__ с точки зрения
> верификации результата шаг упаковки, и у нас он осуществляется **полностью
> вручную** на сегодняшний день, в этой части легко ошибиться. Некоторые
> прыткие мейнтейнеры пишут себе личную автоматизацию этого дела, говорят.
По-хорошему вендоринга быть не должно и в этом плане rust не особо привередлив. Для компиляции любого проекта ему достаточно:
# Либо прямой путь до исходников.
# Либо исходники в системной директории rust. У нас это /usr/lib/rustlib.
# Либо статические бинари с расширением .rlib во всё той же системной директории.
# Либо ссылка на гит репозиторий.
# Либо ссылка на crates.io, который чем-то по своему назначению похож на git.alt.
Подробнее по этому поводу можно почитать здесь:
https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html

Я думаю, что вендоринг можно заменить либо при помощи автоматически генерируемых src.rpm либо при помощи организации своего локального crates.io, опции, спецификации и прочее имеется. Сделать так чтобы cargo генерировал RPM зависимости я думаю несложно, по крайней мере задача должна быть выполнима.
Comment 11 Vitaly Chikunov 2025-08-14 22:15:21 MSK
Вендоринг решает проблему dependency hell на этапе создания пакета.
Comment 12 Сергей Жидких 2025-08-15 09:10:34 MSK
(Ответ для Vitaly Chikunov на комментарий #11)
> Вендоринг решает проблему dependency hell на этапе создания пакета.
И взамен раздувает исходники пакетов и даёт пространство для злоумышленников пользоваться тем фактом, что некоторые пакеты имеют устаревшие зависимости, потенциально содержащие уязвимости, а также лишает возможности самостоятельно патчить зависимости для всех пакетов.

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

Я думаю, что вендоринг в том виде в котором он существует сейчас, это больше проблема нежели чем полноценное решение.