Потоковое локальное хранилище в режиме ядра? - PullRequest
9 голосов
/ 21 марта 2012

Существует ли эквивалентный локальному потоку хранилище (TLS) для драйверов режима ядра в Windows (точнее, Win32)?

Чего я пытаюсь достичь:

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

Использование static / global не является идеальным вариантом (многопоточность, синхронизация объектов и т. Д.).

Если бы это был код пользовательского режима - очевидно, в такой ситуации можно было бы использовать TLS. Но в AFAIK нет функций режима ядра, таких как TlsGetValue / TlsSetValue. И это имеет смысл - чтобы эти функции работали, нужно сначала выделить индекс TLS для всего процесса. Код драйвера OTOH может быть вызван в произвольном потоке, не ограничиваясь конкретным процессом.

Однако на самом деле мне не нужно постоянное хранилище, ориентированное на потоки. Мне просто нужно специфичное для потока хранилище для вызова функций верхнего уровня.

Я думаю, что знаю, как "реализовать" TLS, хотя и хакерским способом. Вместо выделения индекса TLS я всегда буду использовать предопределенный индекс (скажем, index = 0). В функции верхнего уровня я сохраню сохраненное значение TLS и перезапишу его нужным значением. По завершении сохраненное значение будет восстановлено.

К счастью, я знаю, как TLS реализован в Win32. Для каждого потока есть структура TIB (блок информации о потоке). В каждом потоке к нему можно получить доступ с помощью селектора FS:[18h]. TIB содержит (среди прочего) массив, используемый TLS. Все остальное довольно просто.

Однако я бы предпочел использовать официальный API для достижения чего-то похожего.

  • Существует ли официальный API режима ядра для достижения того, что мне нужно?
  • Есть ли причины избегать того, что я планирую делать? Я знаю, что потенциально может быть проблема с повторным входом (то есть, некоторый код вызывает меня, я перезаписываю значение TLS и затем в конечном счете вызываю исходный код, который может полагаться на TLS). Но это не возможно в моем конкретном случае?
  • Есть ли менее грязные способы решить эту проблему?

Заранее спасибо.

P.S. Теоретически можно использовать SEH (в котором также хранится информация для каждого потока). То есть, оберните код верхнего уровня на __try/__except, затем там, где необходима контекстная информация - вызовите исключение продолжение с некоторым параметром, в блоке __except заполните параметр контекстной информацией, и затем возобновите выполнение. И это 100% действительный программный поток, без использования недокументированных функций. Но, тем не менее, это кажется мне отвратительным занятием, не говоря уже о проблемах с производительностью.

Ответы [ 3 ]

8 голосов
/ 21 марта 2012

Вместо использования FS: [18h] вам, вероятно, следует использовать PsGetCurrentThreadTeb. Даже тогда, я думаю, вы будете полагаться на детали, которые могут измениться в будущих выпусках ОС (возможно, включая пакеты обновления).

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

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

5 голосов
/ 06 мая 2012

Не делайте этого с TEB! TIB и TEB являются структурами пользовательского режима. Приложение пользовательского режима может изменять эту информацию по желанию из другого потока / процессора, пока работает ваш драйвер. Это будет уязвимость повышения привилегий в вашем драйвере.

Я бы рекомендовал передать контекстную структуру для эфемерного контекста, связанного с вашим запросом. Если вам нужно что-то более постоянное, вы можете использовать таблицу AVL или таблицу Hash, которую вы очищаете при выходе из потоков.

4 голосов
/ 21 марта 2012

Вы можете создать структуру, которая содержит входящий запрос, и вы передаете его вместо фактического запроса, а затем просто вставляете все необходимые поля.Очевидно, что это не полностью избавляет от необходимости передавать объект, но обычно вы все равно передаете запрос.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...