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-04-24 23:18 MSK (History)
4 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() на всё подряд, у неё стек молча стал исполняемым 😱.

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

Так вот, когда нечто линкуют с -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)...

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