From 3be11f6544485948ba17646ffa7b4242c2c5339a Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Fri, 24 May 2019 08:16:54 -0700 Subject: [PATCH] Increase stack size for IIS Inprocess (#10511) --- .../CommonLib/ConfigurationSection.h | 1 + .../InProcessOptions.cpp | 1 + .../InProcessOptions.h | 7 ++++++ .../inprocessapplication.cpp | 1 + .../Inprocess/StartupTests.cs | 22 ++++++++++++++++++ .../testassets/InProcessWebSite/Startup.cs | 23 +++++++++++++++++++ 6 files changed, 55 insertions(+) diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h index 3eab02bc7c..446f8c7d4b 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h @@ -29,6 +29,7 @@ #define CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE L"disableStartUpErrorPage" #define CS_ENABLED L"enabled" #define CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK L"callStartupHook" +#define CS_ASPNETCORE_HANDLER_STACK_SIZE L"stackSize" class ConfigurationSection: NonCopyable { diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp index d0e264e0f8..01ec10f6f6 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp @@ -59,6 +59,7 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc const auto handlerSettings = aspNetCoreSection->GetKeyValuePairs(CS_ASPNETCORE_HANDLER_SETTINGS); m_fSetCurrentDirectory = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_SET_CURRENT_DIRECTORY).value_or(L"true"), L"true"); m_fCallStartupHook = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK).value_or(L"true"), L"true"); + m_strStackSize = find_element(handlerSettings, CS_ASPNETCORE_HANDLER_STACK_SIZE).value_or(L"1048576"); m_dwStartupTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_STARTUP_TIME_LIMIT) * 1000; m_dwShutdownTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_SHUTDOWN_TIME_LIMIT) * 1000; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h index 6cb8fcceef..61a79664b2 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h @@ -112,6 +112,12 @@ public: return m_bindingInformation; } + std::wstring + QueryStackSize() const + { + return m_strStackSize; + } + InProcessOptions(const ConfigurationSource &configurationSource, IHttpSite* pSite); static @@ -125,6 +131,7 @@ private: std::wstring m_strArguments; std::wstring m_strProcessPath; std::wstring m_struStdoutLogFile; + std::wstring m_strStackSize; bool m_fStdoutLogEnabled; bool m_fDisableStartUpErrorPage; bool m_fSetCurrentDirectory; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp index afb60a8892..aa90367734 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp @@ -253,6 +253,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication() // Used to make .NET Runtime always log to event log when there is an unhandled exception. LOG_LAST_ERROR_IF(!SetEnvironmentVariable(L"COMPlus_UseEntryPointFilter", L"1")); + LOG_LAST_ERROR_IF(!SetEnvironmentVariable(L"COMPlus_DefaultStackSize", m_pConfig->QueryStackSize().c_str())); bool clrThreadExited; { diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs index 009ecd8319..4374ba519a 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs @@ -673,6 +673,28 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests VerifyDotnetRuntimeEventLog(deploymentResult); } + [ConditionalFact] + [RequiresNewHandler] + public async Task StackOverflowIsAvoidedBySettingLargerStack() + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(); + var deploymentResult = await DeployAsync(deploymentParameters); + var result = await deploymentResult.HttpClient.GetAsync("/StackSize"); + Assert.True(result.IsSuccessStatusCode); + } + + [ConditionalFact] + [RequiresNewHandler] + public async Task StackOverflowCanBeSetBySettingLargerStackViaHandlerSetting() + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(); + deploymentParameters.HandlerSettings["stackSize"] = "10000000"; + + var deploymentResult = await DeployAsync(deploymentParameters); + var result = await deploymentResult.HttpClient.GetAsync("/StackSizeLarge"); + Assert.True(result.IsSuccessStatusCode); + } + private static void VerifyDotnetRuntimeEventLog(IISDeploymentResult deploymentResult) { var entries = GetEventLogsFromDotnetRuntime(deploymentResult); diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs index 110ac5e625..656fe0614d 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs @@ -696,6 +696,29 @@ namespace TestSite await server.StopAsync(cts.Token); } + private async Task StackSize(HttpContext ctx) + { + // This would normally stackoverflow if we didn't increase the stack size per thread. + RecursiveFunction(10000); + await ctx.Response.WriteAsync("Hello World"); + } + + private async Task StackSizeLarge(HttpContext ctx) + { + // This would normally stackoverflow if we didn't increase the stack size per thread. + RecursiveFunction(30000); + await ctx.Response.WriteAsync("Hello World"); + } + + private void RecursiveFunction(int i) + { + if (i == 0) + { + return; + } + RecursiveFunction(i - 1); + } + private async Task GetServerVariableStress(HttpContext ctx) { // This test simulates the scenario where native Flush call is being