Проверить дескриптор указателя действителен

Я хочу реализовать библиотеку Microsoft CryptographicServiceProvider, и в настоящее время я думаю о том, как лучше всего работать с дескриптором контекста, который я создаю.

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

Я пришел из управляемого кода, и я не уверен на 100% в отношении многопоточной обработки указателей в C/C++.

Как правило, за создание и уничтожение дескриптора отвечают две функции (CryptAcquireContext, CryptReleaseContext), и все последующие функции CSP используют дескриптор, возвращаемый функцией-создателем.

Я не нашел какой-либо конкретной информации или спецификации от Microsoft, которая бы давала подход к проектированию или правила, как это сделать. Но я провел исследование с другими поставщиками CSP, созданными Microsoft, чтобы выяснить правила проектирования, а именно:

  • Функции должны быть потокобезопасными
  • Дескриптор контекста не будет использоваться совместно между потоками.
  • Если дескриптор контекста недействителен, возврат с ошибкой

Другой поставщик MS CSP вернет действительный указатель в качестве дескриптора или NULL, если нет.

Я не думаю, что вызывающее приложение будет передавать полный мусор, но может случиться так, что оно передаст дескриптор, который уже был освобожден, и моя библиотека должна вернуться с ошибкой.

Это привело меня к трем идеям, как это реализовать:

  1. Просто выделите память моей контекстной структуры с помощью malloc или new и верните необработанный указатель в качестве дескриптора.

    Я могу ожидать, что приложения, которые вызывают мою библиотеку, передают действительный дескриптор. Но если нет, моя библиотека столкнется с неопределенным поведением. Поэтому мне нужно лучшее решение.

  2. Добавьте указатель, который я создаю, в список (std::list, std::map). Поэтому я могу перебрать список, чтобы проверить, существует ли указатель. Доступ к списку охраняется мьютексом.

    Это должно быть безопасно, и регулярное использование API не должно сказываться на производительности. Но в сценарии с сервером терминалов это может быть. В этом случае процесс Windows lsass.exe создает для каждого пользователя, который хочет войти в контекст CSP, отдельный поток и делает около 10 вызовов API для каждого контекста.

    Цель проекта состоит в том, чтобы моя библиотека могла параллельно обрабатывать 300 клиентов. Я не знаю, сколько потоков создано Windows в этом случае.

    Поэтому, если возможно, я бы предпочел реализацию без блокировки.

  3. Я выделяю базовую структуру, которая содержит контрольное значение и указатель фактических данных. Используйте указатель этой структуры в качестве дескриптора контекста.

    typedef struct CSPHandle  
    {  
        int Type; // (eg. magic number CSPContext=0xA1B2C3D4)  
        CSPContextPtr pCSPContext;  
    };
    

    Таким образом, я мог прочитать первый байт переданного указателя и проверить, соответствуют ли данные моему определенному типу. И у меня есть полный контроль над фактическим указателем данных, который устанавливается в NULL, если контекст освобождается. Это хорошая или плохая идея?

Что вы думаете об этом случае? Должен ли я использовать один из этих подходов или есть другое решение?

Спасибо


person Stephan Weitlaner    schedule 04.03.2015    source источник
comment
I can expect that the applications which call my library will pass a valid handle. But if not my library will run into an undefined behaviour. Предположим, вы обнаружили, что переданный дескриптор недействителен. Что будет делать ваш код? Кроме того, вы должны взвесить преимущества и недостатки попытки слишком много держаться за руки. Если пользователи вашего API являются опытными программистами, то может быть достаточно просто задокументировать, что им нужно передать действительный дескриптор, иначе могут произойти плохие вещи.   -  person PaulMcKenzie    schedule 04.03.2015


Ответы (1)


Я нашел решение и отвечу на свой вопрос.

упустил из виду маленькую, но важную деталь

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

Таким образом, Microsoft CSP должен знать и проверять переданный контекст, чтобы получить правильное сопоставление с конкретной библиотекой.

Пример:
1. client->cryptacquirecontext(in cspname, out ctx)
2. MS CSP->загружает libray из cspname
3 . MS CSP->вызывает указатель функции загруженной библиотеки
4. CSP LIB->cryptacquirecontext создает новый контекст
5. MS CSP->получает возвращенный дескриптор csp и сохраняет его в отображении dll
6. MS CSP->возвращает результат вызывающему приложению
7. client->cryptsetprovparam(ctx) // который был создан ранее
8. MS CSP->проверяет, существует ли контекст и какая библиотека отвечает
9. MS CSP->если данный контекст не может быть сопоставлен с dll csp, будет возвращена ошибка, потому что MS CSP не знает, какой указатель функции должен быть вызван.

Так что в этом случае должно быть достаточно просто выделить память. Если клиентское приложение передает недопустимый дескриптор контекста, оно никогда не попадет в библиотеку csp.

Я думаю, что MS CSP использует список с защитой от мьютекса для хранения сопоставлений контекста. Потому что контекст может быть любым, от случайного числа до действительного указателя.

person Stephan Weitlaner    schedule 05.03.2015