Как зафиксировать продолжительность времени внутри анклава?

Измерение времени выполнения является важным аспектом оценки производительности. Теперь я хочу оценить производительность нескольких кодов внутри анклава SGX (доверенной среды выполнения), и я заметил, что Intel SGX предоставляет API под названием «sgx_get_trusted_time()», чтобы разработчики могли получать текущее время из надежного источника. Однако тут у меня возникли проблемы:

1) «sgx_create_pse_session()» требуется перед использованием «sgx_get_trusted_time()», но я всегда получал ошибку «SGX_ERROR_SERVICE_UNAVAILABLE». Я правильно установил и настроил SGX SDK и PSW (в противном случае я не могу использовать службу удаленной аттестации). Я также пытался обновить Management Engine для платформы разработки (Win10 + ThinkPad x270 + CORE i5), но это не сработало;

2) API возвращает время в секундах, что далеко не точно с точки зрения оценки производительности, особенно когда время, прошедшее между двумя вызовами API, тривиально.

Как я могу решить первую проблему и какое-либо решение для более точного измерения времени, прошедшего внутри анклава? Признателен за любое предложение или намек.


person tuziYou    schedule 03.05.2020    source источник
comment
Можете ли вы записать временные метки начала/остановки до/после кода, который вызывает анклав SGX? Код за пределами анклава должен иметь возможность получать точное время настенных часов для одного вызова, включая все накладные расходы SGX. (Я здесь для тегов intel/x86; я не знаю, имеет ли это смысл для работы SGX. Но если вызовы в анклав синхронны, как и системные вызовы, вы можете рассчитать, сколько времени потребуется для возврата .)   -  person Peter Cordes    schedule 03.05.2020
comment
Спасибо за ответ, но я боюсь, что это не решит мою проблему, так как мне нужно погрузиться в анклав и записать время выполнения определенных операций. Чтение доверенного времени из TPM кажется возможным решением (я еще не пробовал).   -  person tuziYou    schedule 04.05.2020
comment
Работает ли инструкция rdtsc внутри анклава? Если это так, вы можете записать это внутри анклава и разместить где-нибудь, чтобы вы могли видеть снаружи. Как получить количество циклов ЦП в x86_64 из C++?. Он настолько точен, насколько это возможно для x86 (только rdpmc для аппаратных счетчиков производительности меньше накладных расходов), но он по-прежнему имеет накладные расходы в десятки тактовых циклов. И TSC тикает с постоянной частотой, поэтому вам нужно контролировать такты турбо / холостого хода. Смотрите мой ответ на этот вопрос.   -  person Peter Cordes    schedule 04.05.2020
comment
Спасибо еще раз. Я попытался использовать рекомендованную вами инструкцию, но, похоже, ее нельзя вызывать внутри анклава из-за строгих ограничений SGX. Это инструкция принадлежит стандартной библиотеке C? Я задаю этот вопрос, поскольку intel sgx позволяет разработчикам использовать только стандартную библиотеку C и определенные библиотеки, которые тщательно пересматриваются Intel (например, sgx-ssl, которая представляет собой библиотеку openssl с поддержкой sgx) внутри анклава.   -  person tuziYou    schedule 06.05.2020
comment
Это инструкция принадлежит стандартной библиотеке C? Этот вопрос даже не имеет смысла. Это инструкция на языке ассемблера/машинном коде (felixcloutier.com/x86/rdtsc), например add eax, [rdi], а не вызов функции другого кода. Однако он микрокодирован, поэтому его можно обрабатывать особым образом в зависимости от режима. (Это есть или может быть для виртуальных машин). software.intel.com/ en-us/forums/ сообщает, что RDTSC и RDTSCP разрешены внутри анклава для процессоров, поддерживающих SGX2 (с учетом значения CR4.TSD).   -  person Peter Cordes    schedule 06.05.2020
comment
Спасибо. Я новичок в языке ассемблера, поэтому мне немного сложно совместить разработку приложения SGX (которая всегда выполняется с использованием C/C++) и языка ассемблера. У меня еще два вопроса: 1. Означает ли использование rdtsc, что мне нужно разработать приложение SGX с использованием языка ассемблера? 2. Процессор должен поддерживать SGX2, без которого нельзя использовать RDTSC/RDTSCP, верно? Кроме того, кажется, что инструкция не может предоставить надежное время, я думаю, было бы лучше обратиться в зону разработчиков Intel для получения дополнительной помощи. Спасибо за ваши предложения!!!   -  person tuziYou    schedule 07.05.2020
comment
Вы можете думать о компиляторах C++ как об удобном способе генерации машинного кода, поскольку я действительно знаю, как работает ассемблер. ЦП всегда фактически выполняет машинный код, независимо от того, сгенерировал ли его компилятор из простого C++, такого как int foo = a + b;, из встроенных функций, таких как long foo = __rdtsc();, или из встроенного ассемблера. Так что нет, вам не нужно писать программу на ассемблере! 2. Да, в конце концов, дело только в том, какие инструкции процессора разрешены при выполнении в режиме SGX. Очевидно, это не всегда включает rdtsc.   -  person Peter Cordes    schedule 07.05.2020
comment
Что касается того, можете ли вы доверять TSC: вы просто делаете это для бенчмаркинга на своей машине разработки, верно? Если вы не делаете ничего странного, __rdtsc() всегда будет увеличиваться на 1 за цикл ссылки. Абсолютное значение начинается с 0 при сбросе ЦП, если с тех пор оно не менялось. Аппаратная виртуализация Intel/AMD может масштабировать и смещать гостевой TSC, и я думаю, что ядро ​​может написать TSC для его сброса через MSR (регистр для конкретной модели). Но это не возможный вектор атаки на ваш реальный код, если вы удалите код бенчмаркинга rdtsc из реального приложения.   -  person Peter Cordes    schedule 07.05.2020
comment
Спасибо еще раз. Так инструкция rdtsc включена компилятором (предположим, что процессор поддерживает SGX2)? Если да, то как мне использовать его для записи времени выполнения кодов, другими словами, каким будет код бенчмаркинга rdtsc? Код или псевдокод приветствуются.   -  person tuziYou    schedule 07.05.2020
comment
То же, что и для любого источника часов: uint64_t start = __rdtsc(); / делать вещи / uint64_t duration = __rdtsc() - start;. См. Как получить количество циклов ЦП в x86_64 из C++? для получения дополнительной информации, включая некоторые предостережения по этому поводу в моем ответе. Если материал очень короткий, вы можете убедиться, что выполнение более ранних инструкций завершено с помощью _mm_lfence(); duration = __rdtsc() - start;, в противном случае exec не по порядку может запустить rdtsc до того, как работа, которую вы рассчитываете, будет завершена. Время будет в эталонных циклах, а не в абсолютных наносекундах.   -  person Peter Cordes    schedule 07.05.2020
comment
Большое спасибо за такой подробный ответ. Еще один вопрос: когда я добавляю предложенный вами код (т.е. uint64_t start = __rdtsc();), требуются заголовочные файлы. По ссылке stackoverflow.com/q/13772567 заголовочный файл может быть ‹intrin.h› или ‹x86intrin.h› , Правильно? Но VS2017 не может решить ни одну из них. Это связано с тем, что я выбираю исполнителя? Другими словами, нужно ли использовать другие упомянутые вами компиляторы, например, gcc/clang/ICC/MSVC?   -  person tuziYou    schedule 07.05.2020
comment
IDK у меня работает на Godbolt как с MSVC, так и с Clang. godbolt.org/z/wK4iuh. Возможно, создание приложения SGX ограничивает MSVC определенным набором заголовков, который не включает intrin.h? Я никогда ничего не делал с SGX, я здесь только для тегов intel/x86. Я немного знаю об этом как о режиме ЦП, потому что знаю о x86 ISA / сборке, но ничего об инструментах для создания приложений SGX. Надеюсь, кто-то еще заметит ваш вопрос и ответит на него; Я изменил тег [x86], и, надеюсь, это заметит больше людей.   -  person Peter Cordes    schedule 07.05.2020
comment
#include ‹intrin.h› хорошо работает за пределами анклава, но не работает внутри анклава, где компилятор всегда выдает ошибку, которая не может разрешить этот заголовочный файл. Я предполагаю, что этот замечательный инструмент нельзя использовать внутри анклава, другими словами, он считается небезопасным для разведки. Я не уверен, есть ли какое-либо другое возможное решение для точной оценки инструкций внутри анклава. Спасибо за вашу помощь, которая действительно расширила мои знания по ассемблеру! С уважением, Вы.   -  person tuziYou    schedule 07.05.2020
comment
другими словами, считается небезопасным для разведки. Я бы не совсем так сформулировал этот вывод. Больше похоже на то, что MSVC не думает, что вы должны использовать какие-либо встроенные функции, или забыли сделать их доступными. Вы можете найти определение __rdtsc в intrin.h и скопировать его в свое приложение SGX. Или, если вы компилируете для 32-битного режима, вы могли бы использовать встроенный ассемблер MSVC. (Встроенная поддержка asm в MSVC была настолько неуклюжей, что ее отключили для 64-битного режима.)   -  person Peter Cordes    schedule 07.05.2020
comment
Позвольте мне кратко представить здесь SGX. Приложение SGX обычно состоит из двух частей: ненадежной части (т. е. исходный файл содержит коды, разрабатываемые для функциональных целей, а не безопасности) и доверенной части (т. е. файл анклава содержит жизненно важные коды, безопасность которых следует тщательно учитывать и защищать). Поэтому доступные заголовочные файлы ограничены: внутри анклава разрешено использовать только то, что считается безопасным. Вот почему я предполагаю, что из-за того, что Intel не поддерживает ‹intrin.h›, ‹intrin.h› доступен за пределами анклава, но не внутри.   -  person tuziYou    schedule 07.05.2020
comment
В любом случае, я попытаюсь скопировать определение rdtsc в анклав.   -  person tuziYou    schedule 07.05.2020
comment
Хорошо, спасибо за это резюме. В целом это имеет смысл, потому что большинство заголовочных файлов имеют прототипы библиотечных функций, которые вам придется вызывать. И, очевидно, вы не можете вызывать printf из анклава или любую другую функцию, объявленную в stdio.h. Но я думаю, что все в intrin.h можно просто встроить в машинную инструкцию. Например, _popcnt_u32(x) можно вычислить с помощью цикла или битхаков; его запрет аналогичен запрету оператора C++ * умножения для целых чисел: вы можете легко программировать без него, просто менее удобно использовать цикл сдвига/сложения, а не более безопасный   -  person Peter Cordes    schedule 07.05.2020
comment
IDK, возможно, в intrin.h есть некоторые вещи, которые имеет смысл не предоставлять, но тогда в качестве побочного эффекта вы теряете доступ к другим вещам. Например, rdtsc и rdrand - это не то, что вы могли бы просто вычислить другим способом, поэтому, возможно, есть какая-то причина их запретить. (По-видимому, SGX полностью запрещал rdtsc до SGX2, а затем только с правильной настройкой в ​​​​управляющем регистре; IDK, что для этого по умолчанию). В общем, для MSVC может иметь смысл защитить вас от вас самих, не позволяя некоторым встроенным функциям находиться в анклаве, но это выбор Microsoft, а не Intel.   -  person Peter Cordes    schedule 07.05.2020
comment
Вы имеете в виду, что вместо этого я реализую rdtsc внутри анклава? Так же, как вы предлагаете копировать определение rdtsc в анклаве?   -  person tuziYou    schedule 07.05.2020
comment
Да, я предлагаю обойти это ограничение компилятора/заголовка и попытаться заставить ваш компилятор выдать инструкцию rdtsc в машинный код вашего анклава, скопировав некоторые строки из intrin.h в ваш собственный заголовок. Я попробовал на Godbolt, используя -E, чтобы заставить компилятор выводить содержимое заголовочного файла. Там есть прототип unsigned __int64 __rdtsc(void);, но использование его вручную просто заставляет его скомпилировать вызов функции с этим именем, а не встроенную инструкцию rdtsc. godbolt.org/z/n3BhEy. Какая-то другая строка должна позволять распознавать его как встроенный компилятор.   -  person Peter Cordes    schedule 07.05.2020
comment
Понятно. Большое спасибо! Я попробую и обновлю вовремя, пока проблема не будет решена.   -  person tuziYou    schedule 07.05.2020


Ответы (1)


Измерение времени выполнения внутри анклава может быть немного сложным. Есть два решения, о которых я могу подумать, и у каждого есть свои плюсы и минусы в зависимости от характера вашего приложения.

Измерение времени выполнения в ненадежном коде с помощью ECALL и OCALL.

Перед запуском функции, производительность которой вы хотите оценить (путем измерения времени ее выполнения), запустите таймер на ненадежном коде.

Таймер можно запустить либо:

  1. Перед тем, как ненадежный код вызовет основную функцию в доверенном коде (чтобы измерить время ее выполнения). Запустите таймер на недоверенном коде, вызовите доверенную функцию (ECALL), когда функция ECALL закончит свое выполнение, остановите таймер на недоверенном коде.
  2. Пока работает ненадежный код. Запустите таймер с помощью OCALL, вызванного доверенным кодом, и остановите таймер с помощью другого OCALL, вызванного доверенным кодом. Ненадежный вызов должен обрабатывать эти OCALL и соответственно запускать/останавливать таймер.

В зависимости от того, в какой части (функции или нескольких функциях) доверенного кода вы хотите измерить время его выполнения, должно работать одно из приведенных выше решений. Еще одна вещь, о которой следует помнить, это то, что вы можете иметь вектор в ненадежном коде, чтобы отслеживать время выполнения нескольких функций. После завершения выполнения кода вы можете распечатать элементы вектора или даже выполнить над ними некоторые вычисления.

Если вы выполняете слишком много ECALL и OCALL, они могут быть немного дорогостоящими. Вы можете либо измерить только время ECALLs и OCALLs и вычесть их из общего времени выполнения, либо проверить решение ниже.

Измерение времени выполнения ненадежного кода с помощью HotCalls.

Горячие вызовы обеспечивают более быстрый интерфейс с анклавом. Эта работа была опубликована Ofir Weisse в Regaining Lost Cycles with HotCalls: A Fast Interface for SGX Secure Enclaves.

Код можно найти в репозитории Ofir. В своем образце кода он измерял время выполнения; поэтому его пример кода может быть очень полезен для вашего случая.

person Kassem    schedule 09.07.2020
comment
Большое спасибо за такой подробный ответ! Во-первых, решение, которое я принимаю в настоящее время, заключается в том, чтобы сначала сравнить все время выполнения, включая время переключения контекста sgx, как первый метод, который вы упомянули выше, и оценить время выполнения кодов внутри анклава исключительно в другом проекте (не ECALL) . Думаю, достаточно указать временные затраты SGX, так как оба проекта работают на одной платформе. Во-вторых, академическое исследование, о котором вы упомянули, является для меня отличным ориентиром, который может стать отправной точкой для суждения о моих собственных результатах оценки. Большое спасибо!!!! - person tuziYou; 09.07.2020