"Исчезает" guardant Code

Обнаружено непонятное поведение, мне кажется, что это ошибка в GuardantAPI или
драйверах.

Условия возникновения: Программа аварийно завершается во время выполнения GrdCodeLoad()
Проблема: При последующих запусках Guardant Code заблокирован, защита блокирует программу.

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

  FAILURE IN : GrdCodeLoad(teh_guardant, 0x00, gcexe.size(), &gcexe[0], NULL)
  ERROR CODE : 23
             : Guardant dongle locked by another copy of protected application

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

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

Результат первого запуска:

    D:\Projects\GrdTest\Release>grdtest
    GrdTest sample application by iRacly 2012
     A initialize and start thread...
     T begin
     T Opening guardant session...
     T logged in, flushing...
     A grd thread is flushing...
     A done, aborting...

Видно, что запустился поток защиты (строки, начинающиеся с T), открыта сессия, логин на ключе, начата загрузка программы. Главный поток получил сигнал о начале загрузки и самоубился. Поведение ожилаемое, всё нормально (пока, вроде бы)

Второй запуск:

    D:\Projects\GrdTest\Release>grdtest
    GrdTest sample application by iRacly 2012
     A initialize and start thread...
     T begin
     T Opening guardant session...
     T logged in, flushing...
     A grd thread is flushing...
      FAILURE IN : GrdCodeLoad(teh_guardant, 0x00, gcexe.size(), &gcexe[0], NULL)
      ERROR CODE : 23
                 : Guardant dongle locked by another copy of protected application
     T guardant ready
     T Using guardant session...
     T Closing guardant session...
     A done, aborting...

Видно, что поток защиты не может загрузить программу в ключ.


Дополнительный наблюдения:

  • Ожидание 15+ минут (как в документации к GrdCloseHandle()) -- ситуация не меняется

  • После переподключения Guardant Code (вытащить/вставить из USB порта) - ключ работает штатно.

  • Попытки вызывать GrdSetDriverMode() положительных результатов не дали


Конфигурация:
GRD API:
    Version: 5.51
    Release: 22.06.2011

    Процессор Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz
    Операционная система Microsoft Windows 7 (Build 7600)
    Версия драйвера Guardant 5.50.85
   
    Модель Guardant Code USB
    Дата и время выпуска 07 Jun 2010 16:20:30
    Версия ключа 0.1
    Тип микроконтроллера 09
    Номер программы 1, 0, 0, 14
   
Диагностика установленных ключей Guardant: (grddiag.exe после запуска теста)
    Создание дескриптора ключа (GrdCreateHandle) Успешно
    Установка режима поиска ключа (GrdSetFindMode) Успешно
    Поиск ключа (GrdFind) Успешно
    Проверка кода доступа к ключу Данная утилита не может произвести подробное тестирование этого ключа.
    Удаление дескриптора ключа (GrdCloseHandle) Успешно

Исходный код теста

// GrdTest.cpp : Defines the entry point for the console application.
//
// iRacly 2012
#include <iostream>
#include <assert.h>
#include <Windows.h>
#include <process.h>
#include <fstream>
#include "GrdAPI\grdapi.h"
#pragma comment (lib, "Grdapi32.lib")


HANDLE beginWriteEvent;

#define VERIFY_GRD(X) { int result = (X);                                   \
    if (GrdE_OK != result) {                                                \
        char szErrorMsg[1024];                                              \
        int nRet = GrdFormatMessage(NULL, result, GrdLng_ENG, szErrorMsg,   \
            1024, NULL);                                                    \
        std::cerr                                                           \
            << "  FAILURE IN : " << #X << std::endl                         \
            << "  ERROR CODE : " << result << std::endl                     \
            << "             : " << szErrorMsg << std::endl;                \
    }                                                                       \
}

unsigned __stdcall GrdThread(void* arguments)
{
    std::cout << " T begin" << std::endl;
    int a, b, c, d;
    {
        std::ifstream codes("codes.txt");
        codes >> a >> b >> c >> d;
    }
    std::string gcexe;
    {
        std::ifstream app("dongle.gcexe", std::ios::binary);
        assert(app.good());
        size_t const chunkSize = 64 * 1024;
        char chunk[chunkSize];
        // Program is 64K max!
        app.read(chunk, chunkSize);
        std::streamsize sz = app.gcount();
        assert(!app.good());
        assert(sz < chunkSize);
        gcexe.assign(chunk, chunk + sz);
    }
    HANDLE teh_guardant;
    unsigned int id = 0;
    BYTE abyGrd[GrdContainerSize];
    {    
        std::cout << " T Opening guardant session..." << std::endl;
        int grdRz;
        grdRz = GrdStartup(GrdFMR_Local);
        assert(GrdE_OK == grdRz);
        teh_guardant = GrdCreateHandle(
            (HANDLE)abyGrd, // Pointer to memory allocated for Grd protected container, if NULL, Grd API allocates memory for new Grd protected container by itself
            GrdCHM_MultiThread,
            NULL); // Reserved and must be NULL
        assert(teh_guardant);
        VERIFY_GRD(GrdSetAccessCodes(teh_guardant, a, b, c, d));
        VERIFY_GRD(GrdSetFindMode(teh_guardant, GrdFMR_Local, 0, 0, 0, 0, 0, 0, 0, 0, GrdFMI_USB));
        TGrdFindInfo GrdFindInfo;
        //always look first and the only key
        VERIFY_GRD(GrdFind(teh_guardant, GrdF_First, (DWORD*)&id, &GrdFindInfo));
        VERIFY_GRD(GrdLogin(teh_guardant, -1, GrdLM_PerStation));
        std::cout << " T logged in, flushing..." << std::endl;
        BOOL resetRz = SetEvent(beginWriteEvent);
        assert(resetRz);
        VERIFY_GRD(GrdCodeLoad(teh_guardant, 0x00, gcexe.size(), &gcexe[0], NULL));
        std::cout << " T guardant ready" << std::endl;
    }
    { // use guardant here
        std::cout << " T Using guardant session..." << std::endl;
    }
    { // finalize
        std::cout << " T Closing guardant session..." << std::endl;
        VERIFY_GRD(GrdCloseHandle(teh_guardant));
        VERIFY_GRD(GrdCleanup());
    }
    std::cout << " T end" << std::endl;
    return 0;
}

int main(int argc, char* argv[])
{
    std::cout << "GrdTest sample application by iRacly 2012" << std::endl;
    std::cout << " A initialize and start thread..." << std::endl;
    beginWriteEvent = CreateEventW(NULL, TRUE, FALSE, L"FlashingGrdProgram");
    assert(beginWriteEvent);
    unsigned threadID = 0;
    HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, &GrdThread, NULL, 0, &threadID);
    DWORD waitRz = WaitForSingleObject(beginWriteEvent, INFINITE);
    assert(WAIT_OBJECT_0 == waitRz);
    std::cout << " A grd thread is flushing..." << std::endl;
    Sleep(30); // this is empirical value: let T print something
    // harakiri
    std::cout << " A done, aborting..." << std::endl;
    TerminateProcess(OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()), 0);
    WaitForSingleObject(hThread, INFINITE);
    return 0;
}

Re: "Исчезает" guardant Code

Спасибо за сообщение, такое поведение действительно может быть следствием ошибки API/драйверов. Однако это так же может быть особенностью микропрограммы. Мы проверим предоставленную Вами информации и сообщим о результатах.

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

Re: "Исчезает" guardant Code

AndreyStepin пишет:

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

Раз уж обращаете внимание, озвучте также критерии по которым описанная ошибка вдруг стала "весьма некритичной" . Вызов функций апи является базовой функциональностью, описан ее отказ. На каком основании драйвер ключа вдруг залочил ключ и оставил сессию при том, что программа, создавшая сессию, прекратила свою работу? Судя по описанию таймаут таже не сработал. По моему ошибка является критичной, и далеко не факт что она относится только лишь к загрузке кода, думаю такое же поведение можно получить и при закрытии программы во время вызова любой другой функции апи.

Re: "Исчезает" guardant Code

Критерий следующий: вызов GrdCodeLoad в подавляющем большинстве случаев производится 1 раз при программировании ключа в GrdUtil и занимает пару секунд. За годы службы ключа у конечного пользователя вызов этой функции может произойти еще несколько раз, при обновлении содержимого ключа. А может и никогда больше не произойти. В итоге получаем что за средний срок эксплуатации ключа 100 миллионов секунд (ключ вотктнут в компьютер в течение 3.5 лет) существует примерно 10-20 "опасных" секунд когда ключ может подвиснуть при аварийном завершении программы во время GrdCodeLoad.

Причем это лечится простым перевтыканием ключа! То есть абсолютно простой и безопасный workaround. На мой взгляд бОльшая проблема здесь именно в аварийном завершении программы разработчика.

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

Re: "Исчезает" guardant Code

AndreyStepin пишет:

Критерий следующий: вызов GrdCodeLoad в подавляющем большинстве случаев производится 1 раз при программировании ключа в GrdUtil и занимает пару секунд. За годы службы ключа у конечного пользователя вызов этой функции может произойти еще несколько раз, при обновлении содержимого ключа. А может и никогда больше не произойти. В итоге получаем что за средний срок эксплуатации ключа 100 миллионов секунд (ключ вотктнут в компьютер в течение 3.5 лет) существует примерно 10-20 "опасных" секунд когда ключ может подвиснуть при аварийном завершении программы во время GrdCodeLoad.

То есть логика такова, что интервал времени, в который возможен сбой, довольно мал по сравнению с временем жизни ключа , и поэтому это как бы не ошибка? А если допустим пользователь вообще не использует подобные функции, то и исправлять не требуется, потому что время использования вообще равно нулю?  Не вижу тут ни логики ни критериев.

AndreyStepin пишет:

Причем это лечится простым перевтыканием ключа! То есть абсолютно простой и безопасный workaround. На мой взгляд бОльшая проблема здесь именно в аварийном завершении программы разработчика.

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

Re: "Исчезает" guardant Code

Если бы проблема ключа приводила к зависанию/падению программы и потере данных пользователя - это была бы однозначно критичная ошибка. Здесь все с точностью до наоборот - проблема программы приводит к зависанию ключей. Причем проблема потенциально крайне редкая. Причем лечится перевтыканием ключа.

Опять же - если ошибка подтвердится на версиях 6.х - она обязательно будет исправлена. Я просто обозначил тот факт, что она по сути не препятствует функционированию ключей в 99.999% случаев, а там где она и проявляется - не вызывает никаких серьезных последствий и элементарно обходится. С моей личной точки зрения это некритично.

Re: "Исчезает" guardant Code

AndreyStepin пишет:

Если бы проблема ключа приводила к зависанию/падению программы и потере данных пользователя - это была бы однозначно критичная ошибка. Здесь все с точностью до наоборот - проблема программы приводит к зависанию ключей. Причем проблема потенциально крайне редкая. Причем лечится перевтыканием ключа.

Опять же - если ошибка подтвердится на версиях 6.х - она обязательно будет исправлена. Я просто обозначил тот факт, что она по сути не препятствует функционированию ключей в 99.999% случаев, а там где она и проявляется - не вызывает никаких серьезных последствий и элементарно обходится. С моей личной точки зрения это некритично.

Нет, я со всем согласен , но смотрите в чем тут дело. Человек описывает проблему, как часть описания он пишет, что если переткнуть ключ то все работает. Зачем ему повторять то, что он и так знает, выдавая за ответ поддержки? Он то создал тему после того как нашел воркараунд, не до, явно его он не устроил.

Re: "Исчезает" guardant Code

Вот это страсти закипели, не ожидал такой бурной дискуссии.

Насчёт корявого решения (воркараунд) - к сожалению не нашёл.

Насчёт критичности ошибки, для нас она не кажется такой уж малозначительной. По следующим причинам: 1) наш продукт - приложение безопасности,  программа запущена постоянно, работает в большом числе случаев в автоматическом режиме (тоесть, оператор может быть, а может и вышел куда, а может и нет оператора -- только периодическое обслуживание); 2) GrdCodeLoad() мы делаем каждый раз при запуске. Если компьютер сбойнул в неподходящий момент а физически находится в запечатаном ящике на вышке в глухом лесу... Тут у наших заказчиков, и у нас, соответственно, - проблемы.

Напоследок, ещё одно наблюдение: установил драйвера 6.00.101 и мой тест стал работать заметно лучше. Теперь после первого запуска (на котором вызывается сбой) на второй запуск тест ругается как и раньше, НО есть отличие: GrdCleanup() явно что-то полезное делает и на третьем запуске программа уже работает корректно. Если ошибка действительно исправлена в новых драйверах, хотелось бы закрепить это достижение на будущие версии.

Резюме:
GRD API:
    Version: 5.51
    Release: 22.06.2011
Версия драйвера Guardant 5.50.85

Работоспособность можно восстановить. Можно ли надеятся на официальную проверку производителем?

Re: "Исчезает" guardant Code

Ираклий,

Как я написал ранее мы проведем этот тест повторно на последних версиях API и драйвера (!). Мы исправляли подобную ошибку в драйвере 6.0, что подтверждается теперь и вашими наблюдениями. Однако конкретно GrdCodeLoad мы так пристально не гоняли на стресс-тесты по причине особенностей использования данной функции.

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

Поробуйте поработать с драйвером 6.0.0.101 - зависаний там быть не должно.

Re: "Исчезает" guardant Code

На данный момоент, к сожалению, не ключ, программа или hardware, а именно драйвер ключа и оказался слабым звеном. Это к тому, что мы получили рекламацию от клиентов о "незапуске", при анализе журналов обнаружили описанную проблему. Самое ужасное, что тупо перезапустить компьютер или передёрнуть ключ некому.

Нам надо получить подтверждение от разработчиков, что в связке с новыми драйверами проблемы зависания нет. Мы не можем тестировать guardant драйвера на наших клиентах, нам нужно минимум второе (кроме собственного) тестирование на отсутствие зависания.

PS: Указано

AndreyStepin пишет:

Мы исправляли подобную ошибку в драйвере 6.0

- можно ознакомиться с деталями?

AndreyStepin пишет:

Ираклий,

Как я написал ранее мы проведем этот тест повторно на последних версиях API и драйвера (!). Мы исправляли подобную ошибку в драйвере 6.0, что подтверждается теперь и вашими наблюдениями. Однако конкретно GrdCodeLoad мы так пристально не гоняли на стресс-тесты по причине особенностей использования данной функции.

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

Поробуйте поработать с драйвером 6.0.0.101 - зависаний там быть не должно.

Re: "Исчезает" guardant Code

Ираклий,

На некоторых (!) серверных (!) чипсетах у нас наблюдалось периодическое зависание ключей Sign/Time/Code (раз в несколько недель при интенсивной работе) из за периодической потери USB-пакетов этими чипсетами. Мы в драйвере 6.0 ввели в протокол общения с ключами некоторую избыточность, в связи с чем зависания по нашим тестам прекратились. Однако один из клиентов тщательно проверивший информацию сообщим нам что зависания все еще бывают, но примерно в 1000 раз реже. Нам не удалось это воспроизвести, и клиент за последние полгода не поднимал этот вопрос.

Главный совет который я могу дать: откажитесь от излишнего использования GrdCodeLoad. В идеале за все время жизни ключа эта функция будет использована всего несколько раз, а во многих случаях всего 1 - при первоначальной прошивке ключа.

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