{"id":15443,"date":"2010-01-01T07:00:00","date_gmt":"2010-01-01T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2010\/01\/01\/your-program-assumes-that-com-output-pointers-are-initialized-on-failure-you-just-dont-realize-it-yet\/"},"modified":"2010-01-01T07:00:00","modified_gmt":"2010-01-01T07:00:00","slug":"your-program-assumes-that-com-output-pointers-are-initialized-on-failure-you-just-dont-realize-it-yet","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20100101-00\/?p=15443","title":{"rendered":"Your program assumes that COM output pointers are initialized on failure; you just don&#039;t realize it yet"},"content":{"rendered":"<p>\nWe saw last time that the COM rules for output pointers are that\nthey must be initialized on return from a function,\neven if the function fails.\nThe COM marshaller relies on this behavior,\nbut then again, so do you; you just don&#8217;t realize it yet.\n<\/p>\n<p>\nIf you use a smart pointer library (be it ATL\nor boost or whatever), you are still relying on output\npointers being <code>NULL<\/code> when not valid,\nregardless of whether or not the call succeeded.\nLet&#8217;s look at this line of code from\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2009\/10\/07\/9904040.aspx\">\nthat article about <code>IUnknown::QueryInterface<\/code><\/a>:\n<\/p>\n<pre>\nCComQIPtr&lt;ISomeInterface&gt; spsi(punkObj);\n...\n\/\/ spsi object goes out of scope\n<\/pre>\n<p>\nIf the <code>IUnknown::QueryInterface<\/code> method puts a\nnon-<code>NULL<\/code> value in <code>spsi<\/code> on failure,\nthen when <code>spsi<\/code> is destructed, it&#8217;s going\nto call <code>IUnknown::Release<\/code> on itself,\nand something bad happens.\nIf you&#8217;re lucky, you will crash because the thing lying around in\n<code>spsi<\/code> was a garbage pointer.\nBut if you&#8217;re not lucky, the thing lying around in <code>spsi<\/code>\nmight be a pointer to a COM object:\n<\/p>\n<pre>\n<i>\/\/ wrong!\nHRESULT CObject::QueryInterface(REFIID riid, void **ppvObj)\n{\n  *ppvObj = this; \/\/ assume success since it almost always succeeds\n  if (riid == IID_IUnknown || riid == IID_IOtherInterface) {\n    AddRef();\n    return S_OK;\n  }\n  \/\/ forgot to set *ppvObj = NULL\n  return E_NOINTERFACE;\n}<\/i>\n<\/pre>\n<p>\nNotice that this code optimistically sets the output pointer to\nitself, but if the interface is not supported, it changes its mind\nand returns <code>E_NOINTERFACE<\/code> <i>without setting the\noutput pointer to <code>NULL<\/code><\/i>.\nNow you have an elusive reference counting bug,\nbecause the destruction of <code>spsi<\/code> will call\n<code>CObject::Release<\/code>,\nwhich will manifest itself by <code>CObject<\/code> object\nbeing destroyed prematurely because you just over-released the object.\nIf you&#8217;re lucky, that&#8217;ll happen relative soon;\nif you&#8217;re not lucky, it won&#8217;t manifest itself for another half hour.\n<\/p>\n<p>\nOkay, sure, maybe this is too obvious a mistake for\n<code>CObject::QueryInterface<\/code>, but any method that\nhas an output parameter can suffer from this error,\nand in those cases it might not be quite so obvious:\n<\/p>\n<pre>\n<i>\/\/ wrong!\nHRESULT CreateSurface(const SURFACEDESC *psd,\n                      ISurface **ppsf)\n{\n *ppsf = new(nothrow) CSurface();\n if (!*ppsf) return E_OUTOFMEMORY;\n HRESULT hr = (*ppsf)-&gt;Initialize(psd);\n if (SUCCEEDED(hr)) return S_OK;\n (*ppsf)-&gt;Release(); \/\/ throw it away\n \/\/ forgot to set *ppsf = NULL\n return hr;\n}<\/i>\n<\/pre>\n<p>\nThis imaginary function\ntakes a surface description and tries to create a surface\nthat matches it.\nIt does this by first creating a blank surface,\nand then initializing the surface.\nIf that succeeds, then we succeed;\notherwise, we clean up the incomplete surface and fail.\n<\/p>\n<p>\nExcept that we forgot to set <code>*ppsf = NULL<\/code>\nin our failure path.\nIf initialization fails, then we destroy the surface,\nand the pointer returned to the caller points to the\nsurface that we abandoned.\nBut the caller shouldn&#8217;t be looking at that pointer because\nthe function failed, right?\n<\/p>\n<p>\nWell, unless the caller called you like this:\n<\/p>\n<pre>\nCComPtr&lt;ISurface&gt; spsf;\nif (SUCCEEDED(CreateSurface(psd, &amp;spsf))) {\n ...\n}\n<\/pre>\n<p>\nIf the surface fails to initialize, then <code>spsf<\/code>\ncontains a pointer to a surface that has already been deleted.\nWhen the <code>spsf<\/code> is destructed, it&#8217;s going to call\n<code>ISurface::Release<\/code> on some point that is no longer\nvalid, and bad things are going to happen.\nThis can get particularly insidious when <code>spsf<\/code> is\nnot a simple local variable but rather a member of\nclass which itself doesn&#8217;t get destroyed for a long time.\nThe bad pointer sits in <code>m_spsf<\/code> like a time bomb.\n<\/p>\n<p>\nAlthough all the examples I gave here involve COM interface pointers,\nthe rule applies to all output parameters.\n<\/p>\n<pre>\nCComBSTR bs;\nif (SUCCEEDED(GetName(&amp;bs)) { ... }\n\/\/ -or-\nCComVariant var;\nif (SUCCEEDED(GetName(&amp;var)) { ... }\n<\/pre>\n<p>\nIn the first case, the the <code>GetName<\/code> method had\nbetter not leave garbage in the output <code>BSTR<\/code> on failure,\nbecause the <code>CComBSTR<\/code>\nis going to <code>SysFreeString<\/code> in its destructor.\nSimilarly in the second case with <code>CComVariant<\/code>\nand <code>VariantClear<\/code>.\n<\/p>\n<p>\nSo remember, if your function doesn&#8217;t want to return a value\nin an output pointer, you still have to return something in it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We saw last time that the COM rules for output pointers are that they must be initialized on return from a function, even if the function fails. The COM marshaller relies on this behavior, but then again, so do you; you just don&#8217;t realize it yet. If you use a smart pointer library (be it [&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-15443","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>We saw last time that the COM rules for output pointers are that they must be initialized on return from a function, even if the function fails. The COM marshaller relies on this behavior, but then again, so do you; you just don&#8217;t realize it yet. If you use a smart pointer library (be it [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/15443","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=15443"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/15443\/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=15443"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=15443"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=15443"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}