Возврат из собственного кода в управляемый приводит к повреждению возвращаемого объекта

Я пытаюсь создать реализацию для аутентификации U2F в своей программе WPF, используя C++/CLI и libu2f-host(https://github.com/Yubico/libu2f-host) У меня есть полностью нативная функция в C++/CLI dll, которая вызывает соответствующие функции libu2f, а затем возвращает указатель на u2f_reply (мой собственный класс) который содержит ответ и код ошибки. Однако, когда этот объект получен управляемой частью dll, код ответа по-прежнему правильный, но строка ответа, похоже, полностью повреждена.

Я попытался немедленно вернуть указатель char, а затем превратить его в управляемую строку с помощью маршала, и я попытался использовать стандартную строку С#. Я попытался использовать memcpy, чтобы скопировать ответ в ответ-> ответ, но безуспешно.

Managed_U2F_Reply^ Utilities::AuthenticateU2F(System::String^ challenge)
{
    IntPtr ptrToChallenge = System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(challenge);
    u2f_reply* reply = NativeUtilities::AuthenticateU2F(static_cast<char*>(ptrToChallenge.ToPointer()));
    //clean up before return
    System::Runtime::InteropServices::Marshal::FreeHGlobal(ptrToChallenge);
    Managed_U2F_Reply^ managedReply = gcnew Managed_U2F_Reply();
    managedReply->ResultCode = reply->responseCode;
    if (managedReply->ResultCode == 0)
    {
        managedReply->Response = gcnew String(reply->response, 0, reply->length);
    }
    return managedReply;
}
struct u2f_reply
{
    int responseCode;
    int length;
    char* response;
};
u2f_reply* NativeUtilities::AuthenticateU2F(char* challenge)
{
    u2f_reply* reply = new u2f_reply();
    char response[2048] = { 0 };
    size_t response_len = sizeof(response);
    u2fh_devs* devs = NULL;
    u2fh_rc rc;
    u2fh_initflags flags = (u2fh_initflags)0;
    rc = u2fh_global_init(flags);
    if (rc != U2FH_OK)
    {
        reply->responseCode = rc;
        return reply;
    }
    rc = u2fh_devs_init(&devs);
    if (rc != U2FH_OK)
    {
        reply->responseCode = rc;
        return reply;
    }
    rc = u2fh_devs_discover(devs, NULL);
    if (rc != U2FH_OK)
    {
        reply->responseCode = rc;
        return reply;
    }
    rc = u2fh_authenticate2(devs, challenge, "https://demo.yubico.com", response, &response_len, U2FH_REQUEST_USER_PRESENCE);
    if (rc != U2FH_OK)
    {
        reply->responseCode = rc;
        return reply;
    }
    u2fh_devs_done(devs);
    u2fh_global_done();
    reply->length = response_len;
    reply->response = response;//adding printf here prints it out correctly, yet it doesn't when I call it in Utilities::AuthenticateU2F
    reply->responseCode = rc;
    return reply;
}

Я ожидаю, что он вернет объект json с данными ответа, как здесь: https://developers.yubico.com/libu2f-host/, но я получаю случайную строку символов ASCII (не могу вставить сюда) и без ошибок


person Matthias Hoste    schedule 20.09.2019    source источник
comment
Это стандартная ошибка с оборванным указателем. Указатель u2f_reply.response указывает на локальную переменную функции, которая больше не выполняется. Имеет тенденцию оставаться незамеченным, но удача всегда заканчивается рано или поздно. Сегодня рано. Вам нужно сделать копию.   -  person Hans Passant    schedule 20.09.2019
comment
@HansPassant Я вижу, никогда не сталкивался с оборванными указателями. Таким образом, возможным решением было бы скопировать его с помощью memcpy?   -  person Matthias Hoste    schedule 20.09.2019
comment
Есть несколько возможных исправлений, но если вы пишете код на C++, вам настоятельно рекомендуется изменить этот элемент char* на std::string.   -  person Hans Passant    schedule 20.09.2019
comment
Использование memcpy сработало (сначала я сделал это не в той функции), но зачем мне это учитывать? Я имею в виду, что я немного новичок в C++, но мне интересно узнать об этом больше.   -  person Matthias Hoste    schedule 20.09.2019


Ответы (1)


Благодаря Хансу Пассанту я исправил проблему, используя:

u2f_reply* NativeUtilities::AuthenticateU2F(char* challenge)
{
    u2f_reply* reply = new u2f_reply();
    char response[2048] = { 0 };
    size_t response_len = sizeof(response);
    u2fh_devs* devs = NULL;
    u2fh_rc rc;
    u2fh_initflags flags = (u2fh_initflags)0;
    rc = u2fh_global_init(flags);
    if (rc != U2FH_OK)
    {
        reply->responseCode = rc;
        return reply;
    }
    rc = u2fh_devs_init(&devs);
    if (rc != U2FH_OK)
    {
        reply->responseCode = rc;
        return reply;
    }
    rc = u2fh_devs_discover(devs, NULL);
    if (rc != U2FH_OK)
    {
        reply->responseCode = rc;
        return reply;
    }
    rc = u2fh_authenticate2(devs, challenge, "https://demo.yubico.com", response, &response_len, U2FH_REQUEST_USER_PRESENCE);
    if (rc != U2FH_OK)
    {
        reply->responseCode = rc;
        return reply;
    }
    u2fh_devs_done(devs);
    u2fh_global_done();
    reply->length = response_len;
    reply->response = new char[response_len];
    memcpy_s(reply->response, response_len, response, response_len);
    reply->responseCode = rc;
    return reply;
}
person Matthias Hoste    schedule 20.09.2019
comment
Теперь у вас есть еще одна утечка памяти, все, что вы выделяете с помощью new, должно быть освобождено с помощью удаления. - person Hans Passant; 20.09.2019
comment
Я вижу, тогда в моем управляемом методе я могу удалить его после помещения в управляемый ответ. Спасибо за помощь :) - person Matthias Hoste; 20.09.2019