{"id":107823,"date":"2023-02-13T07:00:00","date_gmt":"2023-02-13T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107823"},"modified":"2023-02-12T21:10:21","modified_gmt":"2023-02-13T05:10:21","slug":"20230213-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230213-00\/?p=107823","title":{"rendered":"Adventures in application compatibility: The case of the display control panel crash on exit"},"content":{"rendered":"<p>Windows reliability telemetry reported that there were a large number of crashes in the Display control panel. Since these crashes are coming via telemetry and the Windows Error Reporting service, there is no information about what steps are required to reproduce the problem. All we have are crash dumps.<\/p>\n<p>The crash was due to the instruction pointer being in the middle of nowhere. For example, in one dump, it was at address <code>ffffffff`924bbde0<\/code>. Close study shows that this value is suspiciously similar to <code>00007fff`924bbde0<\/code>, which is the address of <code>ntdll!ButtonWndProc_A<\/code>. This tells me that somebody subclassed a button, and then tried to restore the original window procedure, but they messed up and truncated the 64-bit pointer value to a 32-bit signed integer. Bonus insult: Their button is ANSI, not Unicode. It&#8217;s (checks watch) 2023, get with the program. Not everybody who uses a computer speaks English.<\/p>\n<p>To debug this problem, I had to do some triangulation of the crash dumps to look for a third party component that was common to all (or at least most) of the crashes. Since this was a Display control panel, I focused on the video card information, since video card drivers can provide a custom Display control panel plug-in to show off their driver-specific features.<\/p>\n<p>And I found it.<\/p>\n<p>The custom property sheet that comes with one particular video card has a bug in its <code>WM_<wbr \/>DESTROY<\/code> handler: It casts a <code>WNDPROC<\/code> to a 32-bit value, causing the upper 32 bits to be lost.<\/p>\n<p>Here is the reverse-engineered dialog procedure:<\/p>\n<pre>INT_PTR CALLBACK DialogProc(\r\n    HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)\r\n{\r\n  if (uMsg == WM_INITDIALOG) {\r\n        SetWindowLongPtr(hdlg, GWLP_USERDATA,\r\n                ((PROPSHEETPAGE*)lParam)-&gt;lParam);\r\n  }\r\n  MyClass* self = (MyClass*)GetWindowLongPtr(\r\n        hdlg, GWLP_USERDATA);\r\n  return self ? self-&gt;RealDialogProc(hdlg, uMsg, wParam, lParam)\r\n              : FALSE;\r\n}\r\n<\/pre>\n<p>And here is the &#8220;real&#8221; <code>DLGPROC<\/code>:<\/p>\n<pre>INT_PTR CALLBACK MyClass:RealDialogProc(\r\n    HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)\r\n{\r\n  switch (uMsg)\r\n  {\r\n  ...\r\n  case WM_DESTROY:\r\n    SetWindowLongPtr(GetDlgItem(m_dlg, IDC_SOME_BUTTON),\r\n        GWLP_WNDPROC, (LONG)g_originalWndProc);\r\n   ...\r\n  }\r\n}\r\n<\/pre>\n<p>I suspect this code was originally written as 32-bit code, and the line was<\/p>\n<pre>    SetWindowLong(GetDlgItem(m_dlg, IDC_SOME_BUTTON),\r\n        GWL_WNDPROC, (LONG)g_originalWndProc);\r\n<\/pre>\n<p>When porting to 64-bit, the <code>Set\u00adWindow\u00adLong<\/code> becomes <code>Set\u00adWindow\u00adLong\u00adPtr<\/code> to expand the value to 64 bits, and the name of the index changes from <code>GWL_<wbr \/>WNDPROC<\/code> to <code>GWLP_<wbr \/>WNDPROC<\/code>, with the extra P emphasizing that the value should be passed to <code>Get<\/code>\/<code>Set\u00adWindow\u00adLong\u00adPtr<\/code>.<\/p>\n<p>But they forgot to upgrade their cast from <code>(LONG)<\/code> to <code>(LONG_PTR)<\/code>, so they were accidentally truncating their 64-bit value to a sign-extended 32-bit value as part of the restoration.<\/p>\n<p>I went for <a title=\"Microsoft Money crashes during import of account transactions or when changing a payee of a downloaded transaction\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20121113-00\/?p=6103\"> style points<\/a> and came up with a one-byte patch to fix the bug.<\/p>\n<pre>\/\/ rbx = (LONG)g_originalWndProc\r\n48631d33540300  movsxd  rbx,dword ptr [contoso+0x39c50]\r\n<\/pre>\n<p>Patch the second byte from <code>63<\/code> to <code>8b<\/code>:<\/p>\n<pre>\/\/ rbx = (LONG_PTR)g_originalWndProc\r\n488b1d33540300  mov     rbx,qword ptr [contoso+0x39c50]\r\n<\/pre>\n<p>It turns out that all the machines that are hitting this bug are running drivers that are over ten years old. The current drivers don&#8217;t have this bug. In fact, the current drivers don&#8217;t even have the custom control panel extension! In the time since I originally did this investigation, it appears that people finally got their act together and upgraded their video drivers, because there has been only one recorded occurrence of this crash worldwide in the past 30 days.<\/p>\n<p><b>Bonus reading<\/b>: <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20080625-00\/?p=21853\"> The difference between a junior and senior position at a video card company<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When you get a 64-bit pointer, you probably should remember all 64 of the bits.<\/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-107823","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>When you get a 64-bit pointer, you probably should remember all 64 of the bits.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107823","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=107823"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107823\/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=107823"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107823"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107823"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}