Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/AppInstallerCommonCore/HttpClientHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,20 @@ namespace AppInstaller::Http
toThrow = HRESULT_FROM_WIN32(errorValue);
}

THROW_HR_MSG(toThrow, "%hs", exception.what());
// Ensure that sufficient stack space remains to include the message.
// We have seen rare crashes due to running out of stack space in this path
// so we will only include the message if there is enough space.
// PrintLoggingMessage usage in WIL always creates a 4K stack buffer (2K of wchar_t),
// so we leave some on top of that as well as the buffer is kept alive into further calls.
static constexpr size_t s_msgMinimum = 5 * 1024;

if (Runtime::IsStackAvailable(s_msgMinimum))
{
THROW_HR_MSG(toThrow, "%hs", exception.what());
}
else
{
THROW_HR(toThrow);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ namespace AppInstaller::Repository
std::string GetIdentifier() const;

// Get the source's configuration details from settings.
SourceDetails GetDetails() const;
const SourceDetails& GetDetails() const;

// Get the source's information.
SourceInformation GetInformation() const;
Expand Down
2 changes: 1 addition & 1 deletion src/AppInstallerRepositoryCore/RepositorySource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ namespace AppInstaller::Repository
}
}

SourceDetails Source::GetDetails() const
const SourceDetails& Source::GetDetails() const
{
if (m_source)
{
Expand Down
46 changes: 22 additions & 24 deletions src/AppInstallerRepositoryCore/Rest/RestClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,36 +142,34 @@ namespace AppInstaller::Repository::Rest

// Check the cache for a valid information entry
RestInformationCache informationCache;
std::optional<Schema::IRestClient::Information> cachedInformation = informationCache.Get(endpoint, customHeader, caller);
std::optional<Schema::IRestClient::Information> result = informationCache.Get(endpoint, customHeader, caller);

if (cachedInformation)
if (!result)
{
return std::move(cachedInformation).value();
}

// Not in cache, make REST call to retrieve it
auto headers = GetHeaders(customHeader, caller);
CacheControlPolicy cacheControl;

std::optional<web::json::value> response = helper.HandleGet(
endpoint,
headers,
{},
[&](const web::http::http_response& httpResponse)
{
cacheControl = CacheControlPolicy{ httpResponse.headers().cache_control() };
return Http::HttpClientHelper::HttpResponseHandlerResult{ std::nullopt, true };
});
// Not in cache, make REST call to retrieve it
auto headers = GetHeaders(customHeader, caller);
CacheControlPolicy cacheControl;

std::optional<web::json::value> response = helper.HandleGet(
endpoint,
headers,
{},
[&](const web::http::http_response& httpResponse)
{
cacheControl = CacheControlPolicy{ httpResponse.headers().cache_control() };
return Http::HttpClientHelper::HttpResponseHandlerResult{ std::nullopt, true };
});

THROW_HR_IF(APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE, !response);
THROW_HR_IF(APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE, !response);

InformationResponseDeserializer responseDeserializer;
auto result = responseDeserializer.Deserialize(response.value());
InformationResponseDeserializer responseDeserializer;
result = responseDeserializer.Deserialize(response.value());

// Cache the information value as requested
informationCache.Cache(endpoint, customHeader, caller, cacheControl, std::move(response).value());
// Cache the information value as requested
informationCache.Cache(endpoint, customHeader, caller, cacheControl, std::move(response).value());
}

return result;
return std::move(result).value();
}

std::unique_ptr<Schema::IRestClient> RestClient::GetSupportedInterface(
Expand Down
4 changes: 4 additions & 0 deletions src/AppInstallerSharedLib/Public/winget/Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ namespace AppInstaller::Runtime
// 3. the token is not already elevated
bool IsRunningWithLimitedToken();

// Determines if the given amount of stack bytes are available.
// If the answer cannot be determined properly, the return value will be `false`.
DECLSPEC_NOINLINE bool IsStackAvailable(size_t bytes);

// Returns true if this is a release build; false if not.
inline constexpr bool IsReleaseBuild()
{
Expand Down
17 changes: 17 additions & 0 deletions src/AppInstallerSharedLib/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,21 @@ namespace AppInstaller::Runtime
{
return wil::get_token_information<TOKEN_ELEVATION_TYPE>() == TokenElevationTypeLimited;
}

DECLSPEC_NOINLINE bool IsStackAvailable(size_t bytes)
{
// https://devblogs.microsoft.com/oldnewthing/20200610-00/?p=103855
ULONG_PTR low, high;
GetCurrentThreadStackLimits(&low, &high);
auto remaining = reinterpret_cast<ULONG_PTR>(&low) - low;
if (remaining > high - low)
{
// Choosing to return false instead of failing
return false;
}

ULONG guarantee = 0;
SetThreadStackGuarantee(&guarantee);
return remaining >= bytes + guarantee;
}
}
Loading