Site icon Заметки разработчика

Асинхронный COM под Windows Vista и Windows 7

асинхронный COMТехнология COM (Component Object Model) развивается уже почти двадцать лет. До сих пор на ней построена работа подавляющего числа компонентов системы. Думаю, что так и будет продолжаться в ближайшем будущем. Асинхронный COM — это вариант использования этой технологии. Некоторые приложения начала 2000-х использовали его для построения высоконагруженных серверов.

Все не так безоблачно для Асинхронного COM. Использование асинхронного COM было предложено Microsoft при выпуске Windows 2000 и предназначалось для решения одной из проблем при использовании классического COM — подвисание клиента во время ожидания ответа от сервера. Последний может как долго обрабатывать запрос, так и вообще находиться в не рабочем состоянии, что приведет к повисанию клиента.

Все было здорово до момента выпуска Windows Vista. Потом начались проблемы…

Визуально стало наблюдаться странное поведение приложений, использующих асинхронный COM. Причем сбои проявлялись совершенно непредсказуемо. Иногда приложение просто не стартует, иногда вызывает недопустимую операцию и т.д. И только в результате долгой и мучительной работы с отладчиком выяснилось, что проблема — в OLE32.dll. Последняя стала перезаписывать память.

Поиск проблемы

Предлагаю вашему вниманию участок кода функции CAsyncUnknownMgr::IBegin_QueryMultipleInterfaces

      xor   edi,edi
      ...
      lea   ecx,[ebp-8]
      push  ecx
      push  edi
      push  0Ch
      xor   eax,eax
      push  eax
      push  ebx
      call  __allmul
      push  edx
      push  eax
      call  ULongLongToUInt
      cmp   eax,edi
      push  eax
      jge   @f
      ...
@@:   push  edi
      push  dword ptr [g_hHeap]
      call  dword ptr [pfnHeapAlloc]

Если написать это на C, то участок кода будет выглядеть таким образом:

      int cb;
      void *p;
      UINT nBlockSize;
      ULONGLONG ullVar;
      ...
      cb = ULongLongToUInt(12 * ullVar, &nBlockSize);
      p = pfnHeapAlloc(hHeap, 0, cb);

Напомню вам прототипы функций HeapAlloc и ULongLongToUInt:

HRESULT ULongLongToUInt(ULONGLONG ullOperand, UINT *puiResult);
LPVOID  HeapAlloc(HANDLE hHeap, DWORD dwFlags, DWORD dwBytes);

Таким образом, видно, что CAsyncUnknownMgr::IBegin_QueryMultipleInterfaces передает в HeapAlloc в качестве размера блока значение, возвращенное из ULongLongToUInt. А так как ULongLongToUInt возвращает HRESULT а возвращаемый статус тут всегда S_OK, то всегда запрашивается выделение блока нулевого размера. Далее код CAsyncUnknownMgr::IBegin_QueryMultipleInterfaces копирует в этот блок nBlockSize байт данных, что приводит к перезаписываю памяти и, в итоге, к краху приложения в недалеком будущем.

О чем говорит тот факт, что эта проблема была не только пропущена при тестировании и выпуске Windows Vista, но и не исправлена в Windows 7? Вероятно, о том, что сам Microsoft почти не использует асинхронный COM. Либо не использует его в упрощенном виде (см. следующий параграф) без асинхронных QueryInterface. Думаю, что последнее.

Что делать?

Как я уже упоминал, приложения используют асинхронный COM для того, что бы избавиться от подвисания в момент вызова сервера. Повиснуть может не только вызов COM метода, но и попытка запросить интерфейс у сервера. Другими словами, речь об использовании AsyncIUnknown::QueryInterface. Именно с этим проблема. Что интересно, в Windows Vista появился баг с перезаписыванием памяти в результате вызова AsyncIUnknown::QueryInterface, а в Windows 7 вызов AsyncIUnknown::QueryInterface был просто заблокирован (если кому-то интересно, то были внесены соответствующие изменения в NdrpCloneInOnlyCorrArgs). Не исключено, что заблокировано было из-за жутких проявлений IBegin_QueryMultipleInterfaces, суть которых для Microsoft осталась загадкой.

Поскольку наш программный продукт использовался под Windows 2000, XP и по прежнему нуждается в полном использовании асинхронного COM, был написан код по модифицированию кода ole32.dll в момент запуска наших компонентов. При этом была использована технология, описанная мной в статье «Самомодифицирующиеся программы».

Exit mobile version