- BrainTools - https://www.braintools.ru -
Привет! Меня зовут Константин, я занимаюсь безопасностью архитектуры RISC-V в Syntacore. В этой статье я хочу рассказать, какие механизмы аппаратной защиты информации уже есть в архитектуре RISC-V, какие пока только обсуждают и чем за это приходится платить: площадью кристалла, производительностью и пересборкой софта.

Архитектура RISC-V появилась в 2010 году как университетский проект в Калифорнийском университете в Беркли. У его истоков стояли профессор Дэвид Паттерсон и Крсте Асанович. Крсте до сих пор активно участвует в развитии RISC-V: выступает на конференциях и ведет научную работу.
Первую официальную спецификацию опубликовали в 2014 году. Сейчас архитектуре всего 12 лет, что по меркам процессорных архитектур очень мало. Тем не менее в академической среде RISC-V признали быстро, ведь для ее развития не нужно никому платить. Это же привлекло и представителей индустрии: основа бесплатная, а за готовые модули клиенты будут платить.
В основе RISC-V — принцип «ядро плюс модули». На мой взгляд, это главный фактор успешного роста RISC-V на рынке микроконтроллеров. Принцип этот отлично сочетается с концепцией микроконтроллеров: они решают только ограниченный круг задач, а не задачи общего назначения, так что общие алгоритмы здесь ни к чему. Под четко прописанную задачу можно создать максимально быстрое и эффективное железо, в котором нет ничего лишнего.
Первая часть названия процессора обозначает его архитектуру:
RV32I — 32-битная целочисленная базовая система команд.
RV64I — 64-битная версия (наиболее распространена в серверах и десктопах).
RV128I — перспективная 128-битная реализация.
Вторая часть обозначает модули:
I — целочисленные операции, это обязательный базовый профиль, поэтому буква уже есть в названиях архитектур.
M — целочисленное умножение и деление.
A — атомарные операции (для многозадачности [1]).
F — арифметика с плавающей точкой (одинарная точность).
D — арифметика с плавающей точкой (двойная точность, требует F).
C — сжатые инструкции (16-битные) для экономии памяти [2].
Q — арифметика четверной точности (расширение D).
B — битовые операции (опционально).
V — векторные операции (для HPC, AI).
RISC-V сейчас представлена практически везде, где вообще есть микроконтроллеры. Основные области:
интернет вещей;
АСУ ТП и промышленная электроника;
медицинские устройства;
системы безопасности: датчики дыма, системы контроля, камеры.
Среди перспективных областей также выделю автомобильную электронику и искусственный интеллект [3]. Хотя в этих областях пока в основном лидируют процессоры NVIDIA, я надеюсь, что RISC-V сможет их здесь потеснить.

Главный барьер для развития RISC-V — молодость архитектуры. Цикл жизни микроконтроллера составляет около 10 лет: никто не переделывает станок с каждой новой версией процессора. Покупатели процессоров оценивают будущее примерно в таком же горизонте. Вся история RISC-V лишь немного выходит за этот цикл. Отсюда возникает мысль: архитектура еще недостаточно взрослая, с безопасностью, производительностью и прочим могут быть проблемы.
На деле с безопасностью у RISC-V все примерно так же, как у других микроконтроллеров. Группы угроз те же:
уязвимости программного обеспечения: недостаточная валидация данных, ненадлежащая обработка исключений, использование устаревших библиотек;
атаки на аппаратуру: атаки по побочным каналам, glitch-атаки, декорпусирование;
уязвимости в коммуникационных протоколах.
Вот некоторые из уязвимостей, что я обнаружил в базе Common Weakness Enumeration: CWE-1429 [4], CWE-441 [5], CWE-1194 [6], CWE-1319 [7], CWE-1201 [8], CWE-1257 [9], CWE-1419 [10], CWE-1193 [11], CWE-1320 [12], CWE-1299 [13], CWE-1393 [14], CWE-1391 [15], CWE-1252 [16], CWE-1392 [17], CWE-1423 [18], CWE-1332 [19], CWE-1318 [20], CWE-1247 [21], CWE-1384 [22], CWE-1343 [23], CWE-1300 [24], CWE-1432 [25].
Создатели RISC-V, конечно, рассчитывали, что процессор должен быть не только быстрым, дешевым и экономичным, но и безопасным. Я собрал основные аппаратные и программные механизмы безопасности в RISC-V на сегодня и разложил их по вычислительной сложности предполагаемых платформ: микроконтроллеры, персональные устройства, серверы.

Аббревиатур много, поможет глоссарий.
PMP — Physical Memory Protection.
MMU — Memory Management Unit.
SMAP/SMEP — Supervisor Memory.
CHERI — Capability Hardware Enabled RISC-V Instructions.
CFI — Control Flow Integrity.
SS — Shadow Stack.
LP — Landing Pads.
sPMP — Supervisor mode PMP.
SmMTT — Supervisor mode Memory Tracking Table.
CoVE — Confidential VM Extension.
LW Isolation — Lightweight isolation.
Дальше речь пойдет в основном о микроконтроллерах и персональных устройствах. Граница между ними сейчас размывается, настолько производительными сейчас стали персональные устройства и микроконтроллеры.
Уровни привилегий есть у всех процессоров: они нужны, чтобы изолировать недоверенные вычисления от железа на уровне пользователя. В machine mode есть доступ ко всей аппаратуре, и неправильное обращение с этим режимом легко обрушит всю систему.
В колонке слева — то, что обычно есть в микроконтроллерах, справа для полноты картины — уровни при виртуализации. Классический набор для микроконтроллера — M-mode и U-mode. Сейчас к ним добавляется S-mode, уровень операционной системы.
PMP, Physical Memory Protection — на мой взгляд, настоящая имба. Это набор регионов, которые задают доступ каждого харта, вычислителя в системе, к разным областям памяти. Регионов может быть 16 или 64, и 64 обычно никто не делает: это ведет к большим накладным расходам на доступ. Обычно 16 регионов вполне хватает, чтобы построить нормальную систему с изолированным доступом к памяти.

Расширение smepmp запрещает доступ из M-режима, и даже программное обеспечение в M-режиме не может получить доступ к регионам в S/U режимах. Smap/smep запрещает доступ из S-режима: программное обеспечение в S-режиме не может получить доступ к регионам в U-режимах.
Чем хорош PMP? Его можно сконфигурировать так, что после запуска системы никто уже не перепишет эти области. И PMP в принципе нельзя обойти: каждое обращение hart к физической памяти проходит через этот модуль. Один раз сконфигурировали, и сделать с этим ничего нельзя. Поэтому конфигурировать надо правильно.
IOPMP расширяет идею PMP: защищает физическую память от злонамеренных устройств, которые через DMA (Direct Memory Access) могут попасть, например, в read-only область памяти харта и попытаться что-нибудь там сломать.
Экземпляр IOPMP позволяет инициализировать до 65 535 устройств — обычно этого более чем достаточно. Полная модель IOPMP поддерживает 63 домена, но она очень сложна и может сильно снизить производительность. Устройство, которому нужен быстрый доступ к памяти, проходит через все шаги IOPMP и начинает работать медленно. Поэтому модель сокращают до прямых доступов — так тоже можно.
Хорошо бы иметь решения, которые убивают целый класс уязвимостей — например, все связанные с ROP-, JOP- и COP- и прочими гаджетами. В таких уязвимостях злоумышленник перехватывает управление и передает его туда, где лежат кусочки кода, нужные для взлома программы.

Инструкция LPAD обозначает законные точки перехода для инструкций вызова и косвенного перехода в коде приложения. Порядок работы такой:
Компиляторы выдают инструкцию LPAD в качестве первой инструкции функции, получающей адрес, а также для любых целей косвенного перехода.
Харт отслеживает ожидаемое состояние landing pad (ELP), обновляемое с помощью indirect_call или indirect_jump, чтобы проверить наличие команды LPAD в целевой ветке.
Если инструкция в целевой точке не является корректной LPAD, то возникает исключение.
Landing pads, по сути, не дают перейти туда, куда переходить не нужно: отсекает ROP-гаджеты и все подобные на корню. Для реализации landing pads требуется компилятор с их поддержкой: нужно пересобрать библиотеки и включить этот режим в процессоре.

Shadow stack же защищает от взлома стека и перезаписи кода и данных в нем. Рядом с обычным стеком появляется теневой. За размещение адреса возврата в стеках отвечают инструкции вызова. Во время работы программы обычный и теневой стек сверяют. Появляется расхождение — возникает exception, программа завершается. Взломать сам теневой стек невозможно: через MMU для него выставлены такие права доступа, что попасть туда может только выполняющая код программа.
Сравнительно более продвинутые микроконтроллеры поддерживают виртуальную память: могут перенести вычисления на уровне приложения в собственное виртуальное адресное пространство. MMU устанавливает биты доступа в физическое пространство: отображает виртуальное адресное пространство на физическое и задает права доступа к страницам.

В RISC-V выделены три основных размера виртуального адресного пространства — 39, 48 и 57 бит (режимы SV39, SV48 и SV57). Маленькому микроконтроллеру вполне хватит 39 (9 + 9 + 9 +12) бит. С 48 бит работает бо́льшая часть современных решений. 57 бит — это уже сервера, виртуальные системы и другие наиболее требовательные устройства.
IOMMU преследует ту же цель, что и IOPMP, только защищает он виртуальное адресное пространство от злонамеренных устройств. Механизм довольно прост и давно применяется везде, в том числе в операционных системах.

IOMMU использует двухэтапный процесс трансляции адресов для трансляции IOVA (виртуальных адресов ввода/вывода) в SPA и обеспечения защиты памяти для DMA. При этом задействованы те же форматы таблиц страниц, что используются MMU-хартом для трансляции адресов первого и второго этапов.
Тегирование памяти сейчас активно обсуждают, но пока не приняли в спецификации. А приняли только сам механизм, на котором его можно построить. Тегирование зависит от Pointer masking, работает пока только для 64-битной архитектуры и в виртуальной памяти, в связке с расширением zimop.

Я упоминал, что виртуальное адресное пространство использует максимум 57 бит из 64 возможных. Остается 7 бит, в которые можно записать тег. Так вся память тегируется по схеме «замок с ключом»: каждому указателю в неиспользуемых верхних битах присваивается тег указателя (ключ), а в табличке записывают права доступа каждой области. И тогда каждый доступ в память сопровождается проверкой валидности тега: при наличии прав замок отпирается.
Такое тегирование решает, например, проблему use-after-free: если память освободили, в теге будет прописано, что она не используется. Контролирует чтение за границами выделенной области: тег проверяет, в какие границы памяти вы попали. Так закрывается очень большой класс способов взлома.
Микроконтроллеры обычно выполняют команды по очереди и не обладают возможностью out of order вычисления команд. Но о механизме защиты от спекулятивных вычислений стоит упомянуть отдельно. Продвинутые процессоры могут считать ветки наперед еще до того, как решение принято. Если вы знаете, что в такой ситуации в памяти оказывается ключ или другая секретная информация, то до нее можно добраться через атаку по побочным каналам (side-channel attack).

Чтобы этого не случалось, придумали Zisb. Эти барьеры позволяют разработчикам явно контролировать границы спекуляций в CPU-конвейерах. Программист знает, что в некоем месте будет принято решение, ветку считать не надо, и ставит там барьер, ограничивающий спекуляцию. Теперь ветка вычисляться не будет — а чего нет, то и взломать нельзя.
Zisb запрещают использование предсказаний, созданных в одном домене, при выполнении кода в другом домене: это предотвращает утечки данных через побочные каналы (side channels) при транзиентном выполнении инструкций. Zisb не сбрасывают и не очищают предсказания, а дополняют существующие техники — например, маркировку предсказателей информацией о режиме привилегий.
Барьеры, естественно, замедляют выполнение программ, поэтому автоматически их нигде не ставят. С другой стороны, было даже предложение вообще выключить спекулятивные вычисления в RISC-V, но его отклонили: программы станут медленнее. Так что барьеры ставят вручную там, где надо. В остальных местах спекуляция продолжает работать.
RISC-V Worlds — это развитие архитектуры WorldGuard, которую компания SiFive передала сообществу RISC-V. WorldGuard, в свою очередь, развивает технологию ARM TrustZone, что делит систему на два независимых мира: защищенный и обычный.
Идея здесь очень проста. Каждому устройству с доступом к памяти присваивают собственный World ID. Распределение контекстов агентов и ресурсов по мирам выполняется до или во время сброса/загрузки. Это распределение не меняется динамически во время работы системы. При каждом обращении к памяти специальный чекер проверяет, из какого мира пришел запрос и есть ли у этого мира доступ к этой области памяти.

Выглядит просто, но реализовать это в железе довольно сложно: на каждом пути к памяти нужен чекер, и его надо корректно инициализировать. В технологии ARM было два мира, в RISC-V же считается, что не стоит создавать более восьми миров, иначе производительность сильно просядет.
Инструкции CHERI закрывает много проблем: выход за границы, use-after-free и подобные. Но это очень ресурсоемкий инструмент: требует дополнительные 32/64 бита для адреса с разрешениями, типом объекта и границами. Нужна ли микроконтроллеру безопасность такой ценой, стоит решать в каждом случае отдельно.

Безопасность метода опирается на два принципа:
provenance (происхождение) — capability можно создать только из уже существующей capability;
monotonicity (монотонность) — невозможно создать capability с доступами, превосходящими родительскую capability.
«Бесплатно» встроить механизмы безопасности не выйдет. На применимость аппаратных механизмов защиты влияют три фактора.
Сложность реализации в железе. Если механизм реализовать в железе, он займет часть площади кристалла. Либо модель угроз скажет, что на эту угрозу можно закрыть глаза: если и взломают, то ничего особенного там нет. Я видел примеры, где функции безопасности удваивали размер кристалла. Фича кажется простой, но в «железе» сильно разрастается, и маленький кристалл становится большим.
Поддержка в софте. «Железо» может поддерживать механизм, а ваш legacy-код — нет. Некоторые аппаратные функции требуют поддержку в компиляторах, библиотеках, загрузчиках.
Влияние на производительность. Обычный trade-off: либо скорость, либо безопасность. IOPMP, например, сильно замедляет передачу данных от устройства к памяти.
На мой взгляд, архитектура RISC-V превосходно подходит для микроконтроллеров. Схема «ядро плюс расширения» позволяет сконфигурировать процессор под любые задачи. Аппаратная поддержка нужной функциональности для микроконтроллеров уже есть в виде рабочих расширений. Аппаратная защита информации тоже реализована — через PMP, IOPMP, MMU, IOMMU. Сообщество на этом не остановилось и развивает механизмы дальше.
Микроконтроллеры на RISC-V широко представлены на рынке, их активно производят и в России. Год назад я видел, что основными разработчиками процессоров на RISC-V были европейские и американские компании. Сейчас я вижу огромное количество китайских процессоров, которые представляют не только в виде готовых чипов, но и в виде кода. Можно взять его, например, с GitHub и посмотреть, как все устроено.
К развитию подключились и нейросети. В марте нейросеть за 12 часов сгенерировала работающий процессор [26] по спецификации RISC-V. Хоть и не самый производительный, потому что оптимизировать нейросеть еще не умеет. Подозреваю, лет через пять студенты будут невзначай обмениваться репликами типа «слушай, я тут ядро процессора нарисовал, нормально работает».
Если вам интересна работа с RISC-V, обратите внимание [27] на вакансии нашего полупроводникового направления [28]:
Автор: kvnevidin
Источник [32]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/37069
URLs in this post:
[1] многозадачности: http://www.braintools.ru/article/3673
[2] памяти: http://www.braintools.ru/article/4140
[3] интеллект: http://www.braintools.ru/article/7605
[4] CWE-1429: https://cwe.mitre.org/data/definitions/1429.html
[5] CWE-441: https://cwe.mitre.org/data/definitions/441.html
[6] CWE-1194: https://cwe.mitre.org/data/definitions/1194.html
[7] CWE-1319: https://cwe.mitre.org/data/definitions/1319.html
[8] CWE-1201: https://cwe.mitre.org/data/definitions/1201.html
[9] CWE-1257: https://cwe.mitre.org/data/definitions/1257.html
[10] CWE-1419: https://cwe.mitre.org/data/definitions/1419.html
[11] CWE-1193: https://cwe.mitre.org/data/definitions/1193.html
[12] CWE-1320: https://cwe.mitre.org/data/definitions/1320.html
[13] CWE-1299: https://cwe.mitre.org/data/definitions/1299.html
[14] CWE-1393: http://cwe.mitre.org/data/definitions/1393
[15] CWE-1391: https://cwe.mitre.org/data/definitions/1391.html
[16] CWE-1252: https://cwe.mitre.org/data/definitions/1252.html
[17] CWE-1392: https://cwe.mitre.org/data/definitions/1392.html
[18] CWE-1423: https://cwe.mitre.org/data/definitions/1423.html
[19] CWE-1332: https://cwe.mitre.org/data/definitions/1332.html
[20] CWE-1318: https://cwe.mitre.org/data/definitions/1318.html
[21] CWE-1247: https://cwe.mitre.org/data/definitions/1247.html
[22] CWE-1384: https://cwe.mitre.org/data/definitions/1384.html
[23] CWE-1343: https://cwe.mitre.org/data/slices/1343.html
[24] CWE-1300: https://cwe.mitre.org/data/definitions/1300.html
[25] CWE-1432: https://cwe.mitre.org/data/slices/1432.html
[26] сгенерировала работающий процессор: https://arxiv.org/pdf/2603.08716
[27] внимание: http://www.braintools.ru/article/7595
[28] полупроводникового направления: https://careers.yadro.com/team/semiconductors?utm_source=habr&utm_medium=referral&utm_campaign=ysemi_2026&utm_content=riscv-161026&utm_referrer=habr.com
[29] Инженер-программист Embedded (MCU): https://careers.yadro.com/vacancy/102369?utm_source=habr&utm_medium=referral&utm_campaign=ysemi_2026&utm_content=riscv-161026&ut,m_referrer=habr.com
[30] Ведущий инженер системной верификации CPU IP: https://careers.yadro.com/vacancy/102264?utm_source=habr&utm_medium=referral&utm_campaign=ysemi_2026&utm_content=riscv-161026&utm_referrer=habr.com
[31] Инженер по разработке RTL (Wireless: https://careers.yadro.com/vacancy/69894?utm_source=habr&utm_medium=referral&utm_campaign=ysemi_2026&utm_content=riscv-161026&utm_referrer=habr.com
[32] Источник: https://habr.com/ru/companies/yadro/articles/1094970/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1094970
Нажмите здесь для печати.