We have a code base that is rather old that we are migrating to 64-bit to make use of some new 3rd party 64-bit tools. We are running on Windows 7, using VS2010, and code written in C++.
We spent the last two days trying to figure out an issue where a member variable was not getting set to the expected value. Using the VS2010 memory debugger, we were able to see that member variable memory locations were offset by four bytes. Some research lead us to struct alignment and 64 bit.
It turns out the first variable defined in the class was a 32-bit integer, the second variable was a pointer. If we moved the pointer to the top of the class, all member variables aligned up correctly. This fixed the issue.
We also noticed that either
adding a four byte buffer to the top of class "fixed" the issue
setting pack alignment to four bytes also "fixed" the issue.
The question is, how do you detect these errors? We set the warning level to EnabelAllWarnings and tried several static c++ analyzers, but no luck.
One example where this happens. I can't show the other due to an NDA.
class CTaskBar
{
public:
bool CreateTaskbarIcon( HWND hWnd,
HINSTANCE hInstance,
UINT unNotifyMessage,
UINT unIcon,
LPCTSTR szCaption)
{
if (hWnd == NULL)
return false;
if (hInstance == NULL)
return false;
m_hWnd = hWnd;
m_hInstance = hInstance;
m_NIDTaskbarIconData.cbSize = sizeof(NOTIFYICONDATA);
m_NIDTaskbarIconData.hWnd = m_hWnd;
m_NIDTaskbarIconData.uID = IDI_TASKBARICON;
if ( unIcon ) {
if ( m_hTaskbarIcon )
DestroyIcon( m_hTaskbarIcon );
m_hTaskbarIcon = ::LoadIcon( m_hInstance, MAKEINTRESOURCE( unIcon ) );
m_NIDTaskbarIconData.hIcon = m_hTaskbarIcon;
m_NIDTaskbarIconData.uFlags |= NIF_ICON;
}
if ( unNotifyMessage ) {
m_NIDTaskbarIconData.uCallbackMessage = unNotifyMessage;
m_NIDTaskbarIconData.uFlags |= NIF_MESSAGE;
}
if ( szCaption ) {
strcpy(m_NIDTaskbarIconData.szTip, szCaption);
m_NIDTaskbarIconData.uFlags |= NIF_TIP;
}
// The Shell_NotifyIcon call will add the icon to the task bar.
if ( Shell_NotifyIcon(NIM_ADD, &m_NIDTaskbarIconData) )
return true;
return false;
}
private:
NOTIFYICONDATA m_NIDTaskbarIconData;
HINSTANCE m_hInstance;
HWND m_hWnd;
HMENU m_hTaskbarMenu;
HICON m_hTaskbarIcon;
};
Basically, the application crashes at Shell_NotifyIcon deep inside the Windows API calls. If we look in the memory debugger, we can see memory getting set, but offset by four bytes. If I move m_NIDTaskbarIconData to just after m_hTaskbarIcon. Everything looks correct in the memory debugger and no crash.
So we did do a lot of searching on 64-bit C/C++ applications. As Andrey Cpp pointed out and linked to, there is a struct alignment issue based on the order and size of member variables. Unfortunately PVS-Studio did not point to this issue. I don't believe it to be an error on PVS-Studio. Further research, and trial and error, led us to trying to build the project in VS2013. The issue is gone in VS2013. The closest thing I can find to explain why it works in VS2013 is here.
On x64, the object layout of a class may change from previous releases. If it has a virtual function but it doesn’t have a base class that has a virtual function, the object model of the compiler inserts a pointer to a virtual function table after the data member layout. This means the layout may not be optimal in all cases. In previous releases, an optimization for x64 would try to improve the layout for you, but because it failed to work correctly in complex code situations, it was removed in Visual C++ in Visual Studio 2013.
It looks to be a bug in VS2010 that is fixed in VS2013. The memory debugger in VS2013 shows the alignment as I would expect.
Related
I made my MFC application a drop target by deriving the class CDropTarget from COleDropTarget and overriding all necessary functions. Everything works as expected. However, the return value of OnDrop() confuses me. Its description reads:
Nonzero if the drop is successful; otherwise 0.
I don't understand what "successful" means if multiple files are dropped on my application.
For example, consider the following implementation:
BOOL CDropTarget::OnDrop(CWnd* pWnd, COleDataObject* pDataObj, DROPEFFECT tDropEffect, CPoint tPoint)
{
// I left out declaration/definition of hDrop and path for reasons of clarity.
[...]
UINT numHandledFiles = 0;
// Determine the number of dropped files.
UINT numDroppedFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
// Iterate over all dropped files.
for (UINT n = 0; n < numDroppedFiles; n++)
{
// Get the path of the current file from the HDROP structure.
if (DragQueryFile(hDrop, n, path, PATH_MAX) > 0)
{
// Try to handle each dropped file in my function handleFile().
// It returns true if a file could be handled and false otherwise.
// (The latter happens if a file with the wrong type was dropped.)
if (handleFile(path))
numHandledFiles++;
}
}
return ? // See description below.
}
Now assume that my function handleFile() can only handle .png files
and that multiple files with different file types are dropped on my application at once.
How do I replace return ? in the above code correctly? I see two options:
return numHandledFiles > 0; // At least one file could be handled.
And:
return numHandledFiles == numDroppedFiles; // All files could be handled.
I tried both, but when dropping files from Windows Explorer or Total Commander on my application,
I don't notice any difference at all. What effect does the return value have?
When reading MFC documentation leaves you puzzled you should turn to the Windows SDK documentation, as recommended in the link you provided: "For more information, see IDropTarget::Drop in the Windows SDK.":
On return, must contain one of the DROPEFFECT flags, which indicates what the result of the drop operation would be.
Note that IDropTarget::Drop more closely resembles COleDropTarget::OnDropEx, which you should be implementing instead of COleDropTarget::OnDrop. There is no strict rule for the case you describe. However, the DROPEFFECT should match application behavior (i.e. accept or reject).
Folks
I am in the process up upgrading a legacy application from VS2008 to VS2015. It is extremely dependent on ATL and COM. I have noticed a change in _ATL_COM_MODULE70 in atlbase.h that is causing issues.
In ...\9.0\VC\atlmfc\include\atlbase.h it is defined as:
struct _ATL_COM_MODULE70
{
UINT cbSize;
HINSTANCE m_hInstTypeLib;
_ATL_OBJMAP_ENTRY** m_ppAutoObjMapFirst;
_ATL_OBJMAP_ENTRY** m_ppAutoObjMapLast;
CComCriticalSection m_csObjMap;
};
However in ...\14.0\VC\atlmfc\include\atlcom.h it is defined as:
struct _ATL_COM_MODULE70
{
UINT cbSize;
HINSTANCE m_hInstTypeLib;
_ATL_OBJMAP_ENTRY_EX** m_ppAutoObjMapFirst;
_ATL_OBJMAP_ENTRY_EX** m_ppAutoObjMapLast;
CComCriticalSection m_csObjMap;
};
This causes the following code not to compile:
HRESULT FreeThreadSingletons (void)
{
for (_ATL_OBJMAP_ENTRY** ppEntry = _AtlComModule.m_ppAutoObjMapFirst; ppEntry < _AtlComModule.m_ppAutoObjMapLast; ppEntry++)
{
if (*ppEntry != NULL)
{
_ATL_OBJMAP_ENTRY* pEntry = *ppEntry;
CComClassFactoryThreadSingleton *pThread;
if ((pThread = dynamic_cast<CComClassFactoryThreadSingleton *>(pEntry->pCF)) != NULL)
{
pThread->ReleaseAllObjects ();
}
}
}
return S_OK;
}
The reason is because m_ppAutoObjMapFirst is of type _ATL_OBJMAP_ENTRY_EX in VC14 and _ATL_OBJMAP_ENTRY in VC9.
Microsoft's documentation for VS2015 says that the struct should be the same in VC14 as in VC9:
https://msdn.microsoft.com/en-us/library/ayw1b6h5.aspx
Now for the question. Has anyone seen this issue in the past and is there a workaround that does not involve rewriting everything to the _ATL_OBJMAP_ENTRY_EX interface?
Thanks for the response H. Guijit. Here is Microsoft's response:
As you have already noticed, the documentation in MSDN at _ATL_COM_MODULE70 Structure is inaccurate.
_ATL_OBJMAP_ENTRY and _ATL_OBJMAP_ENTRY_EX are typedefs for two different structures defined in atlbase.h
Unfortunately, you have fallen victim to code that relies on internal structures and implementations that are subject to change.
Not the most helpful thing I have ever read..
I have changed the code to retrieve IUnknown from pCache->pCF
HRESULT FreeThreadSingletons (void)
{
for (_ATL_OBJMAP_ENTRY_EX** ppEntry = _AtlComModule.m_ppAutoObjMapFirst; ppEntry < _AtlComModule.m_ppAutoObjMapLast; ppEntry++)
{
if (*ppEntry != NULL)
{
_ATL_OBJMAP_ENTRY_EX* pEntry = *ppEntry;
CComClassFactoryThreadSingleton *pThread;
if ((pThread = dynamic_cast<CComClassFactoryThreadSingleton *>((pEntry->pCache)->pCF)) != NULL)
{
pThread->ReleaseAllObjects ();
}
}
}
return S_OK;
}
I am not sure this will work yet as I'm still battling build problems but hope to run the application up in the next day or two.
You can rewrite it like this:
for(auto ppEntry = _AtlComModule.m_ppAutoObjMapFirst; ppEntry < _AtlComModule.m_ppAutoObjMapLast; ppEntry++)
This is always correct, no matter the type of the pointer.
I suppose you could avoid modifying your source with some preprocessor magic, but I would consider doing so to be a bit of a disaster for maintainability.
As for having seen this in the past... Microsoft quite often extends existing structs and functions, and when they do the extended struct or function gets 'ex' appended to its name. 'ex' structs are usually (always?) identical to the older version, just with extra fields. Same with 'ex' functions, which usually just get some extra parameters.
I'm using GetProcAddress to gain access to a standard Isapi Filter DLL method - the GetFilterVersion method which takes a pointer to a HTTP_FILTER_VERSION structure.
https://msdn.microsoft.com/en-us/library/ms525822(v=vs.90).aspx
https://msdn.microsoft.com/en-us/library/ms525465(v=vs.90).aspx
I've tested the code against a working Isapi filter that I've written and it works fine. I debug the code against an Isapi filter from a vendor (I don't have access to the source code or anything beyond the dll itself) and I get the exception, "access violation writing location". What could be the issue? (Both Isapi filters work in IIS.)
//Attempted to define function ptr several ways
typedef BOOL(__cdecl * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
//typedef BOOL( * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
//typedef BOOL(WINAPI * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
void arbitraryMethod()
{
HINSTANCE hDLL; // Handle to DLL
TRIRIGAISAPIV lpfnDllFunc2; // Function pointer
DWORD lastError;
BOOL uReturnVal2;
hDLL = LoadLibrary(L"iisWASPlugin_http.dll"); //vendor's dll
//hDLL = LoadLibrary(L"KLTWebIsapi.dll //my dll
if (hDLL != NULL)
{
lpfnDllFunc2 = (TRIRIGAISAPIV)GetProcAddress(hDLL, "GetFilterVersion");
if (!lpfnDllFunc2)
{
lastError = GetLastError();
// handle the error
FreeLibrary(hDLL);
//return 1;
}
else
{
HTTP_FILTER_VERSION pVer = { 6 };
//Call the function via pointer; Works with my dll, fails with vendor's
uReturnVal2 = lpfnDllFunc2(&pVer);
//................ HELP!!!!!!!!!!!!!
}
}
}
One issue that I see is that your function pointer declaration is incorrect.
According to the Microsoft documentation, GetFilterVersion is prototyped as:
BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer);
The WINAPI is a Windows macro that is actually defined as __stdcall, thus you are declaring the function pointer incorrectly when you used __cdecl.
What does WINAPI mean?
Thus, your declaration should be:
typedef BOOL(__stdcall * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
It could be that there are actually some additional structure fields filled by the custom filter.
You can try to increase the size of the structure to see if that will work, like for example:
struct HTTP_FILTER_VERSION_EXTRA {
HTTP_FILTER_VERSION v;
char[1024] extra;
};
HTTP_FILTER_VERSION_EXTRA ver;
ver.v.dwServerFilterVersion = 6;
uReturnVal2 = lpfnDllFunc2(&ver.v);
It is sometimes the case with the WinAPI structures that they allow versioning, so adding fields is possible. If the function doesn't then check (or doesn't know) the actual structure version, it might try to use an extended one which might be different than the one supplied - if the size of the supplied struct is then lesser than the structure version the func tries to use, bad things can happen.
Also check if the DLL is 64-bit or 32-bit. You cannot use 64-bit DLL by 32-bit app and vice versa (but I expect that would already fail during the LoadLibrary call).
I was very surprized to see that in the version of Visual C++ that comes with VS2013 the data members of a newly created class seem to be automatically initialized to 0 or null depending on their type. This is new - and very useful! - behaviour in my experience. I have previously only used VC++ Version 4 when writing serious apps and way back in the middle 1990's the initial values were explicitly stated as being undefined/random.
Is this perhaps some helpful attribute of using the debug libraries, or is it possible to rely on null initialization all the time?
As requested, some example code - nothing very exciting I am afraid:
class CData
{
public:
CData();
CData(const CData ©);
~Data();
const CData& operator=(const CData ©);
//Accessors/Mutators follow...
private:
bool Initialize_Data();
//Just giving a couple of examples of data member sets.
char *input_script_name;
int size_input_script_name;
int size_input_script_name_buffer;
char *interpreter_name;
int size_interpreter_name;
int size_interpreter_name_buffer;
};
CData::CData()
{
Initialize_Data();
}
CData::~CData()
{
//Code to store relevent data in registry
//and then free dynamically allocated memory follows...
}
bool CData::Initialize_Data()
{
//Code to retrieve data from registry stored at end of last run follows
//along with routines to check bounds.
//
//At this point, without executing any further a breakpoint is triggered
//and on inspecting data members in a Watch on 'this' I find them
//to be already initialized to either 0 or null respectively.
}
...
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
//Application entry point;
CData application_data; //and away it goes!
//Usual name mutex to prevent multiple instances and message loop follow...
}
As I said VERY basic and I am not illustrating all the code. However, at the point the breakpoint in 'Initialize_Data' is reached - which is immediately on creating the class and BEFORE anything else is executed - all the data members show up as either 0 or null in a Watch. Which is rather surprising!
This is just a coincidence. What you probably observe is that something clears a lot of memory just before your object is initialized, and then your object is placed in that zero-initialized memory. There is no guarantee that this will remain the case, nor will you be able to rely on this on other platforms/compilers. In Debug mode, Visual C++ actually tries to clear to a non-zero bit-pattern, for example.
If you want to zero initialize, you can use the C++ 11 non-static member initializers like this:
char *input_script_name = nullptr;
int size_input_script_name = 0;
int size_input_script_name_buffer = 0;
char *interpreter_name = nullptr;
int size_interpreter_name = 0;
int size_interpreter_name_buffer = 0;
I'm not sure if the current compilers optimize this to a memset if everything is set to 0, but this is the way to go if you have access to a C++11 compiler.
Update
Just checked with Clang 3.4, it does emit a memset if everything is set to 0. GCC initializes using registers, but I suspect that's because my small test case only had ~10 member variables.
I have written a hooking dll using the mhook library. In a spezial case the NtOpenFile() fails when a std::wstring is defined as stack var. Defining it on the heap the code is working.
The code is working without problems except when a certain win32 application (lets call it nuisance.exe) tries to open an existing testfile (like c:\temp\anyfile.log) the access fails. Mostly STATUS_INVALID_ACL (0xC0000077) is returned then.
I have reduced my code line by line and finally found that the error happens when in a called function a std::wstring is defined (this example below). The error happens every time an on different OS's
NTSTATUS NtOpenFileApiHook::NtOpenFileHook(PHANDLE FileHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PIO_STATUS_BLOCK IoStatusBlock,
ULONG ShareAccess,
ULONG OpenOptions
)
{
NTSTATUS Status = STATUS_SUCCESS;
// using this function the call will fail
AfterThis_NtOpenFile_WillFail();
// using this function INSTEAD the call will work
AfterThis_NtOpenFile_WillWork();
// calling the real NtOpenFile using a pointer
// nothing was changed hier, the original parameters are passed
Status = RealNtOpenFile(FileHandle, ...);
return Status;
}
int AfterThis_NtOpenFile_WillFail()
{
std::wstring String = L"";
return 0;
}
int AfterThis_NtOpenFile_WillWork()
{
std::wstring * pString = new std::wstring();
pString->assign(L"");
delete pString;
return 0;
}
I have fixed it this way for this call. But I'm afraid that other functions in other circumstainces could fail so I'm looking for the reason and (probably) for a solution.
Nuisance.exe is a C# application with default stacksize callling a win32 dll about which I know nothing.
If Nuisance.exe was a C++ application, I would imagine that it calls NtOpenFile in a way similar to this, allocating one of pointer parameters on overwritten stack:
POBJECT_ATTRIBUTES MakeObjectAttributes()
{
POBJECT_ATTRIBUTES oa = {...};
return &oa; // Pointer to stack variable - UB
}
...
NtOpenFile(..., MakeObjectAttributes(), ...)
STATUS_INVALID_ACL (0xC0000077) error might suggest that SecurityDescriptor within OBJECT_ATTRIBUTES is allocated this way.
Then it matters how much stack is used by AfterThis_NtOpenFile_WillFail, and it is more than AfterThis_NtOpenFile_WillWork, since std::wstring would be larger than just a couple of pointers due to small string optimization.
If the call chain is always the same, the corruption may be deterministic.
I don't know if code equivalent of returning address of temporary is possible in C#. But the DLL may be in C/C++ or similar language that allows dandling pointers.
To prove/disprove the role of stack, try to allocate other data on stack that has std::wstring size. More precise proof could be checking passed pointer to see if they point to stack area that is about to be overwritten.