Знакомясь с настройками UEFI BIOS материнской платы ASUS Prime TRX40-Pro, как платформы для рабочих станций на базе процессора AMD Threadripper 3970X, трудно было пройти мимо одной загадочной опции. В общем пуле настроек раздела AMD CBS (что расшифровывается как Common BIOS Specifications, хотя понятнее было бы Custom BIOS Settings) притаился пункт меню RedirectForReturnDis, название которого ни о чем не говорит. Попытаемся исследовать роль и место этой опции в экосистеме нового поколения процессоров, построенных на базе архитектуры Zen 2.
![RedirectForReturnDis — this item from a workaround for GCC/C000005 issue for XV Core on CZ A0, setting MSRC001_1029 Decode Configuration (DE_CFG) bit 14 [DecfgNoRdrctForReturns] to 1 RedirectForReturnDis — this item from a workaround for GCC/C000005 issue for XV Core on CZ A0, setting MSRC001_1029 Decode Configuration (DE_CFG) bit 14 [DecfgNoRdrctForReturns] to 1](/sites/default/files/inline-images/AMD_CBS_CCO_Redirect.png)
RedirectForReturnDis — this item from a workaround for GCC/C000005 issue for XV Core on CZ A0, setting MSRC001_1029 Decode Configuration (DE_CFG) bit 14 [DecfgNoRdrctForReturns] to 1
Дословно, в контексте комментария к опции RedirectForReturnDis можно предположить, что речь о проявлении ошибки, допущенной разработчиками процессора, в результате которой, при определенном неблагоприятном стечении обстоятельств, данные, возвращаемые инструкцией чтения, будут искажены. Скорее всего, речь идет о том, что искажение в первую очередь коснулось данных, читаемых из стека в качестве адреса возврата при выполнении инструкций возврата RET (или IRET — возврат из прерывания). В любом случае ситуация возникает как результат неверной синхронизации спекулятивных механизмов. Для исключения этого опасного сценария требуется перенастроить моделезависимый регистр центрального процессора MSR C001_0029h.
Как можно понять из обсуждения в интернете, означенное искажение влияет на устойчивость работы компилятора GCC. В ситуации, когда обновление UEFI недоступно, рекомендуется устанавливать в коде ядра Linux безопасное значение регистра MSRC001_1029:
Workaround for GCC/C000005 issue for XV Core on CZ A0, setting MSRC001_1029 Decode Configuration (DE_CFG) bit 14 [DecfgNoRdrctForReturns] to 1. C000005 is STATUS_ACCESS_VIOLATION (Segfault). Linux kernel can set MSRC001_1029 without UEFI update (wrmsr).
Итак, опция RedirectForReturnDis позволяет предотвратить возникновение ситуаций, при которых проявляется баг, в результате которого вместо данных, обновленных при операции записи, операция чтения может вернуть данные, имевшие место до записи. Вероятно, режим, исключающий сценарий проявления такой ошибки, подразумевает отключение одного из спекулятивных механизмов, призванного повысить уровень параллелизма в обработке последовательности операций чтения и записи. Ошибка в синхронизации этого механизма приводит к тому, что операция чтения по заданному адресу памяти, получает данные, отличные от тех, которые ранее были записаны по этому адресу.
MSRC001_1029 частично документирован в описаниях процессоров AMD .Официальную информацию о его битах 13 и 14, представляющих интерес в рассматриваемом контексте, найти не удалось. По логике и контексту, инициализация данного моделезависимого регистра входит в обязанности UEFI. Одно из немногих официальных упоминаний о MSRC001_1029h есть в документе Managing Speculation on AMD Processors, но там описывается функциональность другого бита [1], управляющего уровнем строгости синхронизации, которую обеспечивает инструкция LFENCE (Load Fence).
Документ Revision Guide for AMD Family 17h Processors на странице 31 также ссылается на моделезависимый регистр центрального процессора MSRC001_0029h, вернее, на его 13-й бит, в то время, когда UEFI камнем преткновения считает 14-й бит. Весьма вероятно, что оба бита используются для отключения некоторых спекулятивных механизмов, изменяющих последовательности операций чтения и записи. Это означает, что их установка в единичное состояние приведет к более стабильной работе процессора, ценой снижения производительности.
Все упоминания связывает общий контекст, в котором можно выделить такие проблемы:
- вероятность искажения данных;
- вероятность намеренной реализации вредоносных сценариев.
Детализируем сказанное
- Логика синхронизации требует поддерживать консистентность при чтении и записи данных, несмотря на наличие спекулятивных механизмов. Точнее, нарушения синхронизации, в результате которых поток увидел собственное неконсистентное состояние не допускаются и корректируются процессором аппаратно, а нарушения синхронизации, в результате которых один поток увидел неконсистентное состояние другого потока, допускаются, не корректируются аппаратно и требуют внимания со стороны программиста или разработчика компилятора. Для их коррекции используются инструкции семейства MFENCE/SFENCE/LFENCE (Memory Fence, Store Fence, Load Fence), создающие так называемый «барьер» для спекулятивных операций доступа к памяти. Смысл такого механизма в том, что инструкции обращения к памяти, расположенные до «барьера» должны быть полностью выполнены до начала обработки инструкций, расположенных после «барьера». Полное выполнение здесь означает наступление последствий операций обращения к памяти, программно-видимых всеми процессорами мультипроцессорной платформы. Если разработчики процессора допустили ошибки в синхронизации, то, возможно, нарушение этих правил приведет к тому, что поток, например, может «потерять» операцию записи и инструкция чтения получит устаревшие данные, имевшие место до записи. Если такой баг обнаружен в в процессорах, уже выпущенных и поставленных потребителю, то спасти ситуацию может разработчик (кастомизатор) UEFI, хотя и ценой некоторого снижения производительности. Для этого спекулятивные операции, в логике синхронизации которых допущены ошибки, запрещаются. Этим и управляет MSR #C001_1029h.
- Для авторов вредоносных сценариев важно то, что используя нарушения синхронизации можно прочитать данные, доступ к которым запрещен согласно законной логике программы. Упрощенный пример: вредоносный код может прочитать пароль из области памяти, уже после того, как ОС обнулила эту область, так как есть вероятность запаздывания наступления программно-видимых последствий операции записи относительно операции чтения. В этом упрощенном примере, по логике программы, обнуление области памяти должно произойти до попытки ее чтения, но в силу спекулятивных механизмов может наступить позже, в результате ранее хранившиеся там данные (например, пароль) будут прочитаны.