我有一个看起来像这样的HttpHandler:
public class GoodIISHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
try
{
try
{
context.Response.End();
}
catch (Exception)
{
}
}
finally
{
Console.WriteLine("Inside finally block");
}
}
public bool IsReusable => true;
}
效果很好!除非在发布模式下编译并启用优化然后执行(在运行带有.NET 4.5.2的IIS 8的Windows Server 2012上)。然后它崩溃了IIS。
发生了未处理的异常,并且该过程已终止。
申请ID:/ LM / W3SVC / 1592535739 / ROOT / HttpHandlerApp
进程ID:648
异常:System.Threading.ThreadAbortException
消息:线程正在中止。
StackTrace:在System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr,HttpContext context)
在System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer,IntPtr nativeRequestContext,IntPtr moduleData,Int32 flags)
at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer,IntPtr nativeRequestContext,IntPtr moduleData,Int32 flags)
和
错误应用程序名称:w3wp.exe,版本:8.0.9200.16384,时间戳:0x50108835
错误模块名称:KERNELBASE.dll,版本:6.2.9200.17366,时间戳:0x554d4531
异常代码:0xe0434352
故障偏移:0x000000000004aea8
错误进程ID:0x288
错误应用程序启动时间:0x01d1ec2c7db735a6
错误申请路径:c:\ windows \ system32 \ inetsrv \ w3wp.exe
错误模块路径:C:\ Windows \ system32 \ KERNELBASE.dll
报告ID:bb7471fa-581f-11e6-93f1-00155d461b1c
错误包全名:
错误包相关的应用程序ID:
我已将问题缩小到特定的IL位,在启用/禁用优化时会发生变化。
以下是ProcessRequest
的IL,它不会导致IIS崩溃:
.method public hidebysig newslot virtual final
instance void ProcessRequest(class [System.Web]System.Web.HttpContext context) cil managed
{
// Code size 30 (0x1e)
.maxstack 1
.try
{
.try
{
IL_0000: ldarg.1
IL_0001: callvirt instance class [System.Web]System.Web.HttpResponse [System.Web]System.Web.HttpContext::get_Response()
IL_0006: callvirt instance void [System.Web]System.Web.HttpResponse::End()
IL_000b: leave.s IL_0010
} // end .try
catch [mscorlib]System.Exception
{
IL_000d: pop
IL_000e: leave.s IL_0010
} // end handler
IL_0010: leave.s IL_001d
} // end .try
finally
{
IL_0012: ldstr "Inside finally block"
IL_0017: call void [mscorlib]System.Console::WriteLine(string)
IL_001c: endfinally
} // end handler
IL_001d: ret
} // end of method GoodIISHandler::ProcessRequest
要使IIS崩溃,请将第19行更改为
IL_000e: leave.s IL_001d
此更改使嵌套的catch块直接退出到方法返回指令,而不访问try..finally的try块中剩余的单个指令。
我的假设是崩溃涉及ThreadAbortException
(其中一个context.Response.End()
将抛出)的魔法自动重新抛出行为以及CLR和/或IIS和/或ASP.NET如何管理那;因此当优化的IL直接从catch块退出到返回指令时,CLR / IIS / ASP.NET在IL_0010
插入的一些假设的中止重置魔术会被错过,而ThreadAbortException会占用整个IIS。
互联网上散布着关于此行为的线索(例如these msdn docs州ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block.
和this blog提及... notably ASP.NET, even aborts individual threads routinely without unloading the domain. They backstop the ThreadAbortExceptions, call ResetAbort on the thread and reuse it or return it to the CLR ThreadPool.
)但我找不到任何关于该机制的具体内容。< / p>
所以我想我的问题是:这里发生了什么?我是否真的发现CLR或IIS或ASP.NET漏洞与IL优化失败的线程中止机制有关?
对于任何感兴趣的人,都可以在https://github.com/jamezor/HttpHandlerRepro找到完全脚本化的repro。
答案 0 :(得分:0)
请勿使用 Response.End()停止处理请求,最好使用 HttpApplication.CompleteRequest()。看看这些帖子: