The bug dozes in the following code:
LRESULT CToolTipCtrl::OnAddTool(WPARAM wParam, LPARAM lParam) { TOOLINFO ti = *(LPTOOLINFO)lParam; <----- HERE if ((ti.hinst == NULL) && (ti.lpszText != LPSTR_TEXTCALLBACK) && (ti.lpszText != NULL)) { void* pv; if (!m_mapString.Lookup(ti.lpszText, pv)) m_mapString.SetAt(ti.lpszText, NULL); // set lpszText to point to the permanent memory associated // with the CString VERIFY(m_mapString.LookupKey(ti.lpszText, ti.lpszText)); } return DefWindowProc(TTM_ADDTOOL, wParam, (LPARAM)&ti); } |
If you look at the definition of TOOLINFO structure closely, you’ll find that this structure has a variable size which depends on the compilation macros defined:
typedef struct { UINT cbSize; UINT uFlags; HWND hwnd; UINT_PTR uId; RECT rect; HINSTANCE hinst; LPTSTR lpszText; #if (_WIN32_IE >= 0x0300) LPARAM lParam; #endif #if (_WIN32_WINNT >= Ox0501) void *lpReserved; #endif } TOOLINFO, *PTOOLINFO, *LPTOOLINFO; |
That is, a structure pointed to by lParam can have a size from 40 to 48 bytes long depending on the compilation macros defined and can’t be just copied to structure created inside the MFC module. Dealing with variable-size structures, you can copy only a common part of a structure or analyze its version first (that is the cbSize field was made for!).
We can easily figure out what TOOLINFO’s size expected by MFC:
mfc71!CToolTipCtrl::OnAddTool: 7c1a21e5 55 push ebp 7c1a21e6 8bec mov ebp,esp 7c1a21e8 83ec30 sub esp,30h 7c1a21eb 53 push ebx 7c1a21ec 56 push esi 7c1a21ed 8b750c mov esi,dword ptr [ebp+0Ch] 7c1a21f0 57 push edi 7c1a21f1 8bd9 mov ebx,ecx 7c1a21f3 6a0c push 0Ch 7c1a21f5 59 pop ecx 7c1a21f6 8d7dd0 lea edi,[ebp-30h] 7c1a21f9 f3a5 rep movs dword ptr es:[edi],dword ptr [esi] |
The size of TOOLINFO inside the MFC module is equal to 0xc*4 = 0x30 = 48 bytes. And the size of TOOLINFO structure inside our product’s module was equal to 44 bytes!
Of course, in most cases nothing criminal will happen. CToolTipCtrl::OnAddTool just copies 4 extra bytes – it’s eventually not even uses it. The only case when the problem may arise – if those 4 extra bytes don’t have read access. Though this situation is hardly possible if your TOOLINFO structure resides on the stack, what about heap memory? With probability far from zero, your structure can occupy last free bytes in a memory page and next page can be not committed yet. This is exactly the case which I encountered. As a result – access violation.
Note that the problem can appear only if you allocate memory for TOOLINFO structure inside your own module, that is, use SendMessage approach.
I found the bug in MFC 7.1 library, but it’s not fixed even in MFC 10.0.
The ways to workaround
There are several obvious ways to workaround the problem:
1. Do not use SendMessage approach to register a tool with a tool tip control. Use CToolTipCtrl::AddTool method instead.
In case if you cannot avoid SendMessage approach (for static controls for example) you can do the following:
2. Compile your module which uses tool tip control with all macros available (_WIN32_IE and _WIN32_WINNT).
3. Reserve extra bytes (4 or 8, depending on your options) which can be copied without impact.
For example like this:
// Extend TOOLINFO on 8 bytes struct TOOLINFO_EX : public TOOLINFO { DWORD m_dwReserve1; DWORD m_dwReserve2; }; // Allocate memory from heap (remember, it’s just example :)) TOOLINFO* pti = new TOOLINFO_EX; // Initialization // Register a tool toolTipCtrl.SendMessage(TTM_ADDTOOL, 0, (LPARAM) pti); |
Hello, did you report the bug to Microsoft?
ReplyDeleteHello. Yes, you can find the answer here: http://social.msdn.microsoft.com/Forums/en-US/vcmfcatl/thread/c1165e34-328a-41e1-85ac-a65a3cad3227
ReplyDeleteThank you, even if I was thinking to http://connect.microsoft.com/
ReplyDeletebecause maybe a bug report at http://connect.microsoft.com/ can get more attention from Microsoft rather than the post in the forum :-)
ReplyDeleteHello.
ReplyDeleteWell… I had some experience reporting the bugs to Microsoft (including their support and escalation service). Unfortunately, can’t state that the results were much better. In all cases I had to handle the problem myself and search for workarounds. From this point of view, if a bug and workaround already found, it’s more efficient to make this information available for community, then to write “just another report” to Microsoft.
But, who knows, maybe you are right ;)
Bed bug is a very difficult pest to control. Choosing a pest control firm is an important decision. τερμιτες φαρμακο κυπρος
ReplyDeleteInsects tend to be annoying and it seems like all they do is fly around and bothers people. There is no way to avoid insects really when going outside. מדביר חולדות
ReplyDelete