From a9a433582950089bc052b0d670f63947882ba2c4 Mon Sep 17 00:00:00 2001 From: Soham Das Date: Tue, 21 Apr 2026 23:43:54 -0700 Subject: [PATCH] Fix crash when user cancels Windows Hello dialog (nullptr from e.what()) When exceptions cross the VTL0/VTL1 boundary, the exception object's message pointer can become nullptr because VTL0 heap memory is not accessible from VTL1. Calling std::string(e.what()) or strlen(e.what()) on a nullptr triggers an access violation that fast-fails the host process. This was reproducible with HostAppUserBound (Create Key -> Cancel) and HostAppUserBoundSignVerify (Sign -> Cancel) sample apps. Fix: Add a safe_what() helper in both userboundkey.vtl1.cpp (3 sites) and userboundkey.vtl0.cpp (5 sites) that returns a fallback string when e.what() is nullptr. Note: This is distinct from the fix in PR #201 which addressed incorrect HRESULT mapping of KeyCredentialStatus enum values. That fix ensured the cancel error is reported as a real failure HRESULT; this fix hardens the catch blocks that log the error before re-throwing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../veil_enclave_lib/userboundkey.vtl1.cpp | 14 ++++++++--- .../src/veil_host_lib/userboundkey.vtl0.cpp | 23 +++++++++++++++---- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/VbsEnclaveSDK/src/veil_enclave_lib/userboundkey.vtl1.cpp b/src/VbsEnclaveSDK/src/veil_enclave_lib/userboundkey.vtl1.cpp index cadde3ae..0abe198e 100644 --- a/src/VbsEnclaveSDK/src/veil_enclave_lib/userboundkey.vtl1.cpp +++ b/src/VbsEnclaveSDK/src/veil_enclave_lib/userboundkey.vtl1.cpp @@ -13,6 +13,14 @@ namespace veil::vtl1::userboundkey { +// Safe wrapper for e.what() — returns fallback when what() is nullptr +// (common when exceptions cross VTL boundaries) +static const char* safe_what(const std::exception& e) noexcept +{ + const char* msg = e.what(); + return msg ? msg : "unknown exception"; +} + using unique_sessionhandle = wil::unique_any; // RAII wrapper for credentials using WIL @@ -480,7 +488,7 @@ wil::secure_vector create_user_bound_key( catch (const std::exception& e) { // Convert exception message to wide string for debug printing - std::string error_msg = e.what(); + std::string error_msg = safe_what(e); std::wstring werror_msg(error_msg.begin(), error_msg.end()); veil::vtl1::vtl0_functions::internal::debug_print((L"ERROR: create_user_bound_key - Exception caught: " + werror_msg).c_str()); throw; // Re-throw the exception @@ -676,7 +684,7 @@ std::vector load_user_bound_key( catch (const std::exception& e) { // Convert exception message to wide string for debug printing - std::string error_msg = e.what(); + std::string error_msg = safe_what(e); std::wstring werror_msg(error_msg.begin(), error_msg.end()); veil::vtl1::vtl0_functions::internal::debug_print((L"ERROR: load_user_bound_key - Exception caught: " + werror_msg).c_str()); throw; // Re-throw the exception @@ -719,7 +727,7 @@ std::vector reseal_user_bound_key( catch (const std::exception& e) { // Convert exception message to wide string for debug printing - std::string error_msg = e.what(); + std::string error_msg = safe_what(e); std::wstring werror_msg(error_msg.begin(), error_msg.end()); veil::vtl1::vtl0_functions::internal::debug_print((L"ERROR: reseal_user_bound_key - Exception caught: " + werror_msg).c_str()); throw; // Re-throw the exception diff --git a/src/VbsEnclaveSDK/src/veil_host_lib/userboundkey.vtl0.cpp b/src/VbsEnclaveSDK/src/veil_host_lib/userboundkey.vtl0.cpp index e9690553..0084db11 100644 --- a/src/VbsEnclaveSDK/src/veil_host_lib/userboundkey.vtl0.cpp +++ b/src/VbsEnclaveSDK/src/veil_host_lib/userboundkey.vtl0.cpp @@ -36,6 +36,14 @@ using namespace winrt::Windows::Security::Credentials; using namespace veil::vtl0::implementation::debug; +// Safe wrapper for e.what() — returns fallback when what() is nullptr +// (common when exceptions cross VTL boundaries) +static const char* safe_what(const std::exception& e) noexcept +{ + const char* msg = e.what(); + return msg ? msg : "unknown exception"; +} + namespace veil::vtl0::userboundkey::implementation { // RAII wrapper that stores both the session handle and enclave pointer @@ -87,7 +95,8 @@ class unique_sessionhandle catch (const std::exception& e) { std::wstring errorMsg = L"ERROR: Exception during VTL0 session cleanup: "; - errorMsg += std::wstring(e.what(), e.what() + strlen(e.what())); + const char* what_msg = safe_what(e); + errorMsg += std::wstring(what_msg, what_msg + strlen(what_msg)); veil::vtl0::implementation::debug::internal::debug_wprint(errorMsg); } catch (...) @@ -169,7 +178,8 @@ CreateChallengeCallback(std::shared_ptr veil_abi::Untrusted::Implementation::userboundkey_get_autho catch (const std::exception& e) { std::wstring errorMsg = L"DEBUG: Exception in userboundkey_get_authorization_context_from_credential: "; - errorMsg += std::wstring(e.what(), e.what() + strlen(e.what())); + const char* what_msg = safe_what(e); + errorMsg += std::wstring(what_msg, what_msg + strlen(what_msg)); veil::vtl0::implementation::debug::internal::debug_wprint(errorMsg); throw; } @@ -447,7 +458,8 @@ std::vector veil_abi::Untrusted::Implementation::userboundkey_get_secre catch (const std::exception& e) { std::wstring errorMsg = L"DEBUG: Exception in userboundkey_get_secret_from_credential: "; - errorMsg += std::wstring(e.what(), e.what() + strlen(e.what())); + const char* what_msg = safe_what(e); + errorMsg += std::wstring(what_msg, what_msg + strlen(what_msg)); veil::vtl0::implementation::debug::internal::debug_wprint(errorMsg); throw; } @@ -493,7 +505,8 @@ void veil_abi::Untrusted::Implementation::userboundkey_delete_credential(uintptr catch (const std::exception& e) { std::wstring errorMsg = L"DEBUG: Exception in userboundkey_delete_credential: "; - errorMsg += std::wstring(e.what(), e.what() + strlen(e.what())); + const char* what_msg = safe_what(e); + errorMsg += std::wstring(what_msg, what_msg + strlen(what_msg)); veil::vtl0::implementation::debug::internal::debug_wprint(errorMsg); } catch (...)