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

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

Проблема молодости архитектуры
Главный барьер для развития RISC-V — молодость архитектуры. Цикл жизни микроконтроллера составляет около 10 лет: никто не переделывает станок с каждой новой версией процессора. Покупатели процессоров оценивают будущее примерно в таком же горизонте. Вся история RISC-V лишь немного выходит за этот цикл. Отсюда возникает мысль: архитектура еще недостаточно взрослая, с безопасностью, производительностью и прочим могут быть проблемы.
На деле с безопасностью у RISC-V все примерно так же, как у других микроконтроллеров. Группы угроз те же:
уязвимости программного обеспечения: недостаточная валидация данных, ненадлежащая обработка исключений, использование устаревших библиотек;
атаки на аппаратуру: атаки по побочным каналам, glitch-атаки, декорпусирование;
уязвимости в коммуникационных протоколах.
Вот некоторые из уязвимостей, что я обнаружил в базе Common Weakness Enumeration: CWE-1429, CWE-441, CWE-1194, CWE-1319, CWE-1201, CWE-1257, CWE-1419, CWE-1193, CWE-1320, CWE-1299, CWE-1393, CWE-1391, CWE-1252, CWE-1392, CWE-1423, CWE-1332, CWE-1318, CWE-1247, CWE-1384, CWE-1343, CWE-1300, CWE-1432.
Карта механизмов защиты
Создатели 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 — физическая защита памяти
PMP, Physical Memory Protection — на мой взгляд, настоящая имба. Это набор регионов, которые задают доступ каждого харта, вычислителя в системе, к разным областям памяти. Регионов может быть 16 или 64, и 64 обычно никто не делает: это ведет к большим накладным расходам на доступ. Обычно 16 регионов вполне хватает, чтобы построить нормальную систему с изолированным доступом к памяти.

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

Экземпляр IOPMP позволяет инициализировать до 65 535 устройств — обычно этого более чем достаточно. Полная модель IOPMP поддерживает 63 домена, но она очень сложна и может сильно снизить производительность. Устройство, которому нужен быстрый доступ к памяти, проходит через все шаги IOPMP и начинает работать медленно. Поэтому модель сокращают до прямых доступов — так тоже можно.
Landing pads и shadow stack — для целостности потока управления
Хорошо бы иметь решения, которые убивают целый класс уязвимостей — например, все связанные с ROP-, JOP- и COP- и прочими гаджетами. В таких уязвимостях злоумышленник перехватывает управление и передает его туда, где лежат кусочки кода, нужные для взлома программы.

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

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

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

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

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

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

Выглядит просто, но реализовать это в железе довольно сложно: на каждом пути к памяти нужен чекер, и его надо корректно инициализировать. В технологии ARM было два мира, в RISC-V же считается, что не стоит создавать более восьми миров, иначе производительность сильно просядет.
CHERI — Capability Hardware Enhanced RISC-V Instructions
Инструкции 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 часов сгенерировала работающий процессор по спецификации RISC-V. Хоть и не самый производительный, потому что оптимизировать нейросеть еще не умеет. Подозреваю, лет через пять студенты будут невзначай обмениваться репликами типа «слушай, я тут ядро процессора нарисовал, нормально работает».
Если вам интересна работа с RISC-V, обратите внимание на вакансии нашего полупроводникового направления:
jaha33
При всех перспективах risc-v, удивительно что крупные "западные" вендоры применяют их в микроконтроллерах достаточно неохотно и осторожно, или вообще не применяют (например STM, TI, Nordic).
Да и китайских вендоров делающих микросхемы на Risc-V тоже не то чтобы очень много (относительно всех китайских вендоров). У китайцев это Espressif, GigaDevice, WCH, Allwinner. Остальные до сих пор предпочитают кортексы.
А с начала "экспансии" risc-v уже лет 8 прошло, а то и больше.