关于控制面板扩展中指针截断的推测
背景与摘要: 本文探讨了在将旧有的 32 位 Windows 代码迁移至 64 位架构时的一个常见陷阱。文章分析了一位开发者在更新窗口过程句柄时,是如何沦为“不完全重构”的牺牲品的——仅仅通过重命名常量来解决编译器报错,却未能解决由不正确的类型转换所导致的潜在指针截断问题。
Summary
This article explores a common pitfall during the migration of legacy 32-bit Windows code to 64-bit architectures. It examines how a developer, while updating window procedure handles, likely fell victim to "partial refactoring"—fixing compiler errors by renaming constants without addressing the underlying pointer truncation caused by incorrect type casting.
Bug 剖析
在之前的调查中,我们发现了控制面板扩展中由指针截断引起的一个崩溃问题。尽管拥有一个有效的 64 位指针,但应用程序却丢弃了高 32 位的数据,从而导致无效的内存访问。
The Anatomy of the Bug
In a previous investigation, we identified a crash in a control panel extension caused by pointer truncation. Despite having a valid 64-bit pointer, the application discarded the top 32 bits, leading to an invalid memory access.
其根本原因很可能源于从 32 位代码向 64 位代码的过渡。
The root cause likely stems from a transition from 32-bit to 64-bit code.
1. 旧有的 32 位代码
最初,这段代码在 32 位环境中运行得完美无缺:
1. The Legacy 32-bit Code
Originally, the code functioned perfectly in a 32-bit environment:
HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON);
SetWindowLong(hwndButton, GWL_WNDPROC, (LONG)g_originalWndProc);
2. 迁移错误
当针对 64 位系统重新编译时,编译器会将 GWL_WNDPROC 标记为未声明的标识符。开发者遵循文档说明,将该常量更新为其 64 位的等效项 GWLP_WNDPROC。然而,他们却忘记了更新类型转换:
2. The Migration Error
When recompiling for 64-bit, the compiler flags
GWL_WNDPROCas an undeclared identifier. The developer, following documentation, updates the constant to the 64-bit equivalent,GWLP_WNDPROC. However, they fail to update the cast:
// 开发者修复了常量,但留下了 (LONG) 的类型转换
SetWindowLong(hwndButton, GWLP_WNDPROC, (LONG)g_originalWndProc);
3. 必需的修复
重命名这些常量的初衷是作为给开发者的一个“警告标志”。为了支持 64 位指针,类型转换也必须更新为 LONG_PTR:
3. The Required Fix
The renaming of these constants was intended to act as a "warning sign" for developers. To support 64-bit pointers, the cast must also be updated to
LONG_PTR:
SetWindowLong(hwndButton, GWLP_WNDPROC, (LONG_PTR)g_originalWndProc);
为什么会发生这种情况?
这似乎是一个孤立的疏忽,而非系统性的失败。开发者在代码的其他地方成功地实现了窗口子类化:
Why Did This Happen?
It appears this was an isolated oversight rather than a systemic failure. The developer successfully implemented the window subclassing elsewhere in the code:
WNDPROC g_originalWndProc;
HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON);
g_originalWndProc = (WNDPROC)SetWindowLong(hwndButton, GWLP_WNDPROC,
(LONG_PTR)subclassWndProc);
很有可能的情况是,开发者通过重命名常量解决了编译器的报错后分心了,从而忽略了更新相关的类型转换,让应用程序暴露在了被截断的风险之中。
It is highly probable that the developer addressed the compiler error by renaming the constant, got distracted, and neglected to update the associated type cast, leaving the application vulnerable to truncation.
下期预告:我们将探讨为什么这个特定的 Bug 能在代码库中潜伏这么久。
Next time: We will explore why this specific bug has persisted in the codebase for so long.
Source: The Old New Thing