{"id":6653,"date":"2012-09-10T07:00:00","date_gmt":"2012-09-10T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2012\/09\/10\/when-you-transfer-control-across-stack-frames-all-the-frames-in-between-need-to-be-in-on-the-joke\/"},"modified":"2012-09-10T07:00:00","modified_gmt":"2012-09-10T07:00:00","slug":"when-you-transfer-control-across-stack-frames-all-the-frames-in-between-need-to-be-in-on-the-joke","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20120910-00\/?p=6653","title":{"rendered":"When you transfer control across stack frames, all the frames in between need to be in on the joke"},"content":{"rendered":"<p>\nChris Hill suggests\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/07\/20\/10040074.aspx#10042978\">\ndiscussing the use of structured exception handling as it relates\nto the window manager, and specifically the implications for\napplications which raise exceptions from a callback<\/a>.\n<\/p>\n<p>\nIf you plan on raising\nan exception and handling it in a function higher up the stack,\nall the stack frames in between need to be be in on your little scheme,\nbecause they need to be able to unwind.\n(And I don&#8217;t mean &#8220;unwind&#8221; in the &#8220;have a beer and watch some football&#8221;\nsense of &#8220;unwind&#8221;.)\n<\/p>\n<p>\nIf you wrote all the code in between the point the exception is\nraised and the point it is handled, then you&#8217;re in good shape,\nbecause at least then you have a chance of making sure they all\nunwind properly.\nThis means either using RAII techniques (and possibly compiling with\nthe <code>\/EHa<\/code> flag to convert asynchronous exceptions\nto synchronous ones, so that Win32 exceptions will also trigger\nunwind; although that has its own problems since the C++ exception\nmodel is synchronous, not asynchronous)\nor judiciously using <code>try<\/code>\/<code>finally<\/code> (or\nwhatever equivalent exists in your programming language of choice)\nto clean up resources in the event of an unwind.\n<\/p>\n<p>\nBut if you don&#8217;t control all the frames in between, then you\ncan&#8217;t really guarantee that they were written in the style you want.\n<\/p>\n<p>\nIn Win32, exceptions are considered to be horrific situations\nthat usually indicate some sort of fatal error.\nThere may be some select cases where exceptions can be handled,\nbut those are more the unusual cases than the rule.\nMost of the time, an exception means that something terrible\nhas happened and you&#8217;re out of luck.\nThe best you can hope for at this point is a controlled crash landing.\n<\/p>\n<p>\nAs a result of this overall mindset, Win32 code doesn&#8217;t worry\ntoo much about recovering from exceptions.\nIf an exception happens, then it means your process is already\ntoast and there&#8217;s no point trying to fix it,\nbecause that would be\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2009\/11\/13\/9921676.aspx\">\ntrying to reason about a total breakdown of normal functioning<\/a>.\nAs a general rule\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2006\/12\/11\/1259599.aspx#1264901\">\ngeneric Win32 code is not exception-safe<\/a>.\n<\/p>\n<p>\nConsider a function like this:\n<\/p>\n<pre>\nstruct BLORP\n{\n    int Type;\n    int Count;\n    int Data;\n};\nCRITICAL_SECTION g_csGlobal; \/\/ assume somebody initialized this\nBLORP g_Blorp; \/\/ protected by g_csGlobal\nvoid SetCurrentBlorp(const BLORP *pBlorp)\n{\n    EnterCriticalSection(&amp;g_csGlobal);\n    g_Blorp = *pBlorp;\n    LeaveCriticalSection(&amp;g_csGlobal);\n}\nvoid GetCurrentBlorp(BLORP *pBlorp)\n{\n    EnterCriticalSection(&amp;g_csGlobal);\n    *pBlorp = g_Blorp;\n    LeaveCriticalSection(&amp;g_csGlobal);\n}\n<\/pre>\n<p>\nThese are perfectly fine-looking functions from a traditional Win32 standpoint.\nThey take a critical section, copy some data, and leave the\ncritical section.\nThe only thing&sup1; that could go wrong is\nthat the caller passed a bad pointer.\nIn the case of <code>Terminate&shy;Thread<\/code>,\nwe&#8217;re already in the world of &#8220;don&#8217;t do that&#8221;\nIf that happens, a <code>STATUS_ACCESS_VIOLATION<\/code>\nexception is raised, and the application dies.\n<\/p>\n<p>\nBut what if your program decides to handle the access violation?\nMaybe <code>pBlorp<\/code> points into a memory-mapped file,\nand there is an I\/O error paging the memory in,\nsay because it&#8217;s a file on the network and there was a network\nhiccup.\nNow you have two problems:\nThe critical section is orphaned, and the data is only partially\ncopied.\n(The partial-copy case happens if the <code>pBlorp<\/code> points to\na <code>BLORP<\/code> that straddles a page boundary, where the first\npage is valid but the second page isn&#8217;t.)\nJust converting this code to RAII solves the first problem,\nbut it doesn&#8217;t solve the second, which is kind of bad because\nthe second problem is what the critical section was trying to prevent\nfrom happening in the first place!\n<\/p>\n<pre>\nvoid SetCurrentBlorp(const BLORP *pBlorp)\n{\n    CriticalSectionLock lock(&amp;g_csGlobal);\n    g_Blorp = *pBlorp;\n}\nvoid GetCurrentBlorp(BLORP *pBlorp)\n{\n    CriticalSectionLock lock(&amp;g_csGlobal);\n    *pBlorp = g_Blorp;\n}\n<\/pre>\n<p>\nSuppose somebody calls <code>Set&shy;Current&shy;Blorp<\/code> with\na <code>BLORP<\/code> whose <code>Type<\/code> and <code>Count<\/code>\nare in readable memory, but whose <code>Data<\/code> is not.\nThe code enters the critical section, copies the <code>Type<\/code>\nand <code>Count<\/code>, but crashes when it tries to copy the\n<code>Data<\/code>,\nresulting in a\n<code>STATUS_ACCESS_VIOLATION<\/code> exception.\nNow suppose that somebody unwisely decides to handle this exception.\nThe RAII code releases\nthe critical section (assuming that you compiled with <code>\/EHa<\/code>),\nbut there&#8217;s no code to try to patch up the now-corrupted\n<code>g_Blorp<\/code>.\nSince the critical section was probably added to prevent <code>g_Blorp<\/code>\nfrom getting corrupted,\nthe result is that the thing you tried to protect against ended up\nhappening anyway.\n<\/p>\n<p>\nOkay, that was a bit of a digression.\nThe point is that unless everybody between the point the exception\nis raised and the point the exception is handled is in on the joke,\nyou are unlikely to escape fully unscathed.\nThis is particular true in the generalized Win32 case,\nsince it is perfectly legal to write Win32 code in languages\nother than C++,\nas long as you adhere to the Win32 ABI.\n(I&#8217;m led to believe that Visual Basic is still a popular language.)\n<\/p>\n<p>\nThere are a lot of ways of getting stack frames beyond your control\nbetween the point the exception is raised and the point it is handled.\nFor example, you might call\n<code>Enum&shy;Windows<\/code> and raise an exception in the\ncallback function and try to catch it in the caller.\nOr you might raise an exception in a window procedure and try to\ncatch it in your message loop.\nOr you might try to <code>longjmp<\/code> out of a window procedure.\nAll of these end up raising an exception and catching it in another\nframe.\nAnd since you don&#8217;t control all the frames in between,\nyou can&#8217;t guarantee that they are all prepared to resume execution\nin the face of an exception.\n<\/p>\n<p>\n<b>Bonus reading<\/b>:\nMy colleague\n<a HREF=\"http:\/\/paulbetts.org\/\">\nPaul Betts<\/a> has written up\n<a HREF=\"http:\/\/blog.paulbetts.org\/index.php\/2010\/07\/20\/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64\/\">\na rather detailed study of one particular instance of this phenomenon<\/a>.\n<\/p>\n<p>\n&sup1;Okay, another thing that could go wrong is\nthat somebody calls\n<code>Terminate&shy;Thread<\/code> on the thread,\nbut whoever did that knew they were corrupting the process.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Chris Hill suggests discussing the use of structured exception handling as it relates to the window manager, and specifically the implications for applications which raise exceptions from a callback. If you plan on raising an exception and handling it in a function higher up the stack, all the stack frames in between need to be [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-6653","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Chris Hill suggests discussing the use of structured exception handling as it relates to the window manager, and specifically the implications for applications which raise exceptions from a callback. If you plan on raising an exception and handling it in a function higher up the stack, all the stack frames in between need to be [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/6653","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=6653"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/6653\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=6653"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=6653"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=6653"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}