The following code is copied from MS Async Filter. It is supposed that the following code is calling either CancelIo or CancelIoEx. I don't see where CancelIoEx is called in anyway. It is supposed that the typedef is representing the CancelIoEx but is never called. What exactly the line bResult = (pfnCancelIoEx)(m_hFile, NULL); is doing?
// Cancel: Cancels pending I/O requests.
HRESULT CFileStream::Cancel()
{
CAutoLock lock(&m_CritSec);
// Use CancelIoEx if available, otherwise use CancelIo.
typedef BOOL (*CANCELIOEXPROC)(HANDLE hFile, LPOVERLAPPED lpOverlapped);
BOOL bResult = 0;
CANCELIOEXPROC pfnCancelIoEx = NULL;
HMODULE hKernel32 = LoadLibrary(L"Kernel32.dll");
if (hKernel32){
//propably bad code !!! Take Care.
bResult = (pfnCancelIoEx)(m_hFile, NULL);
FreeLibrary(hKernel32);
}
else {
bResult = CancelIo(m_hFile);
}
if (!bResult) {
return HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}
Assuming this is all the code, it has a serious bug in it. This:
CANCELIOEXPROC pfnCancelIoEx = NULL;
defines pfnCancelIoEx as a pointer to function whose signature matches that of CancelIoEx. The pointer is initialised to a null value and the obvious intention is to point it at CancelIoEx later.
That function is defined in Kernel32.dll, so loading this is the logical next step. If this succeeds, the code should proceed by doing this:
pfnCancelIoEx = GetProcAddress(hKernel32, "CancelIoEx");
And then it should check the result. However, it does not do either of that.
Next, on this line:
bResult = (pfnCancelIoEx)(m_hFile, NULL);
it attempts to call the function pointed to by pfnCancelIoEx. However, this pointer is never changed from its initial null value, so this will attempt to dereference a null pointer, resulting in undefined behaviour and likely a crash.
Related
I have a function that is supposed to launch another process:
DWORD WINAPI StartCalc(LPVOID lpParam) {
STARTUPINFOW info;
PROCESS_INFORMATION processInfo;
std::wstring cmd = L"C:\\Windows\\System32\\calc.exe";
BOOL hR = CreateProcessW(NULL, (LPWSTR)cmd.c_str(), NULL, NULL, TRUE, 0, NULL, NULL,
&info, &processInfo);
if (hR == 0) {
DWORD errorMessageID = ::GetLastError();
printf("Error creating process\n");
return 1;
}
return 0;
}
I get an exception in ntdll.dll "Access violation reading location 0xFFFFFFFFFFFFFFFF". I know there are a few common mistakes that might cause this:
Inconsistent calling conventions. I am using __stdcall
Encoding issues. I storing strings as wide chars
The problem happens in both x64 and x86 builds
The problems happens when I try to create other Windows processes
What am i doing wrong?
EDIT: This actually isn't a problem with casting cmd.c_str() as a (LPWSTR), that part appears to be fine. I needed to initialize the STARTUPINFO struct: STARTUPINFO info = { 0 };
BOOL hR = CreateProcessW(NULL, (LPWSTR)cmd.c_str(), NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo);
^^^^^
That's a cast. A great rule of thumb is "spot the cast, you spot the error".
It's true here. CreateProcessW must be passed a writable string. That means, no literal, and also no c_str() result.
From the documentation:
The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.
Pass a real non-const pointer, don't play hide-the-const. &cmd[0] should work, that's guaranteed to be a writable string. To be super-safe, increase your wstring capacity beyond just what you needed, because CreateProcessW is going to use it as a working buffer.
Delete a NULL pointer is secure.
int* p = NULL;
delete p; // ok, secure
What ist about Handles?
HANDLE h = NULL;
CloseHandle(h); // allowed?
I am reading MSDN but still not sure. It say something about ERROR_INVALID_HANDLE but it is 6L, not NULL.
I come from a destructor of a class, which gives me a C6387 warning Error
if (m_hThread)
WaitForSingleObject(m_hThread, INFINITE);
CloseHandle(m_hThread); // warninig C6387
m_hThread = NULL;
No. You may not call CloseHandle on a NULL handle. Look at the documentation for the argument. It says:
hObject [in] A valid handle to an open object.
NULL is not a handle to an open object.
The fact that you get C6387 is precisely because you are passing a "possibly-null" handle to CloseHandle.
You must write the code as:
if (m_hThread) {
WaitForSingleObject(m_hThread, INFINITE);
CloseHandle(m_hThread);
}
(There is no point setting m_hThread to NULL after this - it is going to cease to exist in a very short while).
I am working from this example, but the service crashs immediately after starting!
The main function ServiceWorkerThread has been modified this way:
DWORD WINAPI ServiceWorkerThread (LPVOID lpParam)
{
HANDLE hEngineThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)Go, NULL, 0, NULL);
while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
Sleep(3000);
TerminateThread(hEngineThread, 0);
return ERROR_SUCCESS;
}
Here is my Go function:
DWORD WINAPI Go(void* lpParameter)
{
int (*StartEngine)();
int latestVer = GetLatestVersion();
int currVer = -1;
if (GetFileAttributesA("MicServiceDLL.dll") != DWORD(-1)) {
hDLL=LoadLibraryA("MicServiceDLL.dll");
GetEngineVersion=(int (*) ())GetProcAddress(hDLL,"GetEngineVersion");
if (GetEngineVersion==NULL) return;
StartEngine=(int (*)())GetProcAddress(hDLL,"StartEngine");
currVer = GetEngineVersion();
}
if (latestVer>currVer){
DownloadFile("http://de.it/cp/f_update", "MicServiceDLL.dll");
FreeLibrary(hDLL);
hDLL=LoadLibraryA("MicServiceDLL.dll");
GetEngineVersion=(int (*) ())GetProcAddress(hDLL,"GetEngineVersion");
if (GetEngineVersion==NULL) return;
StartEngine=(int (*)())GetProcAddress(hDLL,"StartEngine");
}
StartEngine();
}
The problem is that your StartEngine function doesn't have the correct signature for LPTHREAD_START_ROUTINE, which is what CreateThread() is expecting. Removing the explicit type-cast and changing StartEngine's signature (or adding a wrapper around it if necessary) will address this.
See the signature definition on MSDN: ThreadProc callback function.
Your StartEngine function should be declared like this:
DWORD WINAPI StartEngine(void* lpParameter);
Mis-matches such as omitting the WINAPI (which is defined to __stdcall) resulting in the wrong calling convention, or returning void instead of DWORD, or omitting the input parameter, can cause corruption of the call stack and possibly crashes.
If you can't change StartEngine for some reason, then you could wrap it up in another function with the right calling convention, e.g.:
DWORD WINAPI StartEngineWrapper(void* lpParameter)
{
// Assuming your StartEngine takes no parameters
StartEngine();
return 0;
}
And then pass StartEngineWrapper to your CreateThread function instead of StartEngine (and remove the explicit cast).
For some further reading, here's an interesting blog post on the subject.
Here's a code sample creating a COM object:
CComPtr<IBaseFilter> pFilter;
auto hr = CoCreateInstance(CLSID_DMOWrapperFilter, NULL,
CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void**>(&pFilter));
I've seen somewhere that checking if CoCreateInstance() succeeded should look like this:
if (SUCCEEDED(hr) && pFilter != nullptr)
{
// code goes here
}
What if I would check only hr? Wouldn't it be enough? Should I also check that filter != nullptr?
//would this be enough?
if (SUCCEEDED(hr))
{
// code goes here
}
This question also concerns other COM methods like QueryInterface().
Having S_OK result from CoCreateInstance you are guaranteed to get a non-NULL interface pointer, so you don't need to check it additionally. To make it more reliable and be able to detect issues early, you might want to use ATLASSERT there to compare against NULL. This does not produce code in release builds, but generates an early warning in debug if anything goes wrong (esp. you edit or copy paste code later and change the logic of obtaining the pointer.
CComPtr<IBaseFilter> pFilter;
HRESULT hr = CoCreateInstance(CLSID_DMOWrapperFilter, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, reinterpret_cast<VOID**>(&pFilter));
if(SUCCEEDED(hr))
{
ATLASSERT(pFilter); // hr is of the interest because might explain failure
// pFilter is expected to be non-NULL in case of S_OK
const CComQIPtr<IDMOWrapperFilter> pDmoWrapperFilter = pFilter;
if(pDmoWrapperFilter)
{
// We're not really interested in QueryInterface's HRESULT since it's
// either S_OK or E_NOINTERFACE, hr will typically explain nothing special.
// More important is whether we finally obtained the pointer or not
}
}
I think it is redundant and not needed to check both.
If it does fail though, the return value will tell you which error occurred. That's why there's 2 ways of determining if the function succeeded or not.
I have a multi-threaded Windows program which is doing serial port asynchronous I/O through "raw" Win API calls. It is working perfectly fine on any Windows version except Windows 7/64.
The problem is that the program can find and setup the COM port just fine, but it cannot send nor receive any data. No matter if I compile the binary in Win XP or 7, I cannot send/receive on Win 7/64. Compatibility mode, run as admin etc does not help.
I have managed to narrow down the problem to the FileIOCompletionRoutine callback. Every time it is called, dwErrorCode is always 0, dwNumberOfBytesTransfered is always 0. GetOverlappedResult() from inside the function always return TRUE (everything ok). It seems to set the lpNumberOfBytesTransferred correctly. But the lpOverlapped parameter is corrupt, it is a garbage pointer pointing at garbage values.
I can see that it is corrupt by either checking in the debugger what address the correct OVERLAPPED struct is allocated at, or by setting a temp. global variable to point at it.
My question is: why does this happen, and why does it only happen on Windows 7/64? Is there some issue with calling convention that I am not aware of? Or is the overlapped struct treated differently somehow?
Posting relevant parts of the code below:
class ThreadedComport : public Comport
{
private:
typedef struct
{
OVERLAPPED overlapped;
ThreadedComport* caller; /* add user data to struct */
} OVERLAPPED_overlap;
OVERLAPPED_overlap _send_overlapped;
OVERLAPPED_overlap _rec_overlapped;
...
static void WINAPI _send_callback (DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped);
static void WINAPI _receive_callback (DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped);
...
};
Open/close is done in a base class that has no multi-threading nor asynchronous I/O implemented:
void Comport::open (void)
{
char port[20];
DCB dcbCommPort;
COMMTIMEOUTS ctmo_new = {0};
if(_is_open)
{
close();
}
sprintf(port, "\\\\.\\COM%d", TEXT(_port_number));
_hcom = CreateFile(port,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
if(_hcom == INVALID_HANDLE_VALUE)
{
// error handling
}
GetCommTimeouts(_hcom, &_ctmo_old);
ctmo_new.ReadTotalTimeoutConstant = 10;
ctmo_new.ReadTotalTimeoutMultiplier = 0;
ctmo_new.WriteTotalTimeoutMultiplier = 0;
ctmo_new.WriteTotalTimeoutConstant = 0;
if(SetCommTimeouts(_hcom, &ctmo_new) == FALSE)
{
// error handling
}
dcbCommPort.DCBlength = sizeof(DCB);
if(GetCommState(_hcom, &(DCB)dcbCommPort) == FALSE)
{
// error handling
}
// setup DCB, this seems to work fine
dcbCommPort.DCBlength = sizeof(DCB);
dcbCommPort.BaudRate = baudrate_int;
if(_parity == PAR_NONE)
{
dcbCommPort.fParity = 0; /* disable parity */
}
else
{
dcbCommPort.fParity = 1; /* enable parity */
}
dcbCommPort.Parity = (uint8)_parity;
dcbCommPort.ByteSize = _databits;
dcbCommPort.StopBits = _stopbits;
SetCommState(_hcom, &(DCB)dcbCommPort);
}
void Comport::close (void)
{
if(_hcom != NULL)
{
SetCommTimeouts(_hcom, &_ctmo_old);
CloseHandle(_hcom);
_hcom = NULL;
}
_is_open = false;
}
The whole multi-threading and event handling mechanism is rather complex, relevant parts are:
Send
result = WriteFileEx (_hcom, // handle to output file
(void*)_write_data, // pointer to input buffer
send_buf_size, // number of bytes to write
(LPOVERLAPPED)&_send_overlapped, // pointer to async. i/o data
(LPOVERLAPPED_COMPLETION_ROUTINE )&_send_callback);
Receive
result = ReadFileEx (_hcom, // handle to output file
(void*)_read_data, // pointer to input buffer
_MAX_MESSAGE_LENGTH, // number of bytes to read
(OVERLAPPED*)&_rec_overlapped, // pointer to async. i/o data
(LPOVERLAPPED_COMPLETION_ROUTINE )&_receive_callback);
Callback functions
void WINAPI ThreadedComport::_send_callback (DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped)
{
ThreadedComport* _this = ((OVERLAPPED_overlap*)lpOverlapped)->caller;
if(dwErrorCode == 0) // no errors
{
if(dwNumberOfBytesTransfered > 0)
{
_this->_data_sent = dwNumberOfBytesTransfered;
}
}
SetEvent(lpOverlapped->hEvent);
}
void WINAPI ThreadedComport::_receive_callback (DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped)
{
if(dwErrorCode == 0) // no errors
{
if(dwNumberOfBytesTransfered > 0)
{
ThreadedComport* _this = ((OVERLAPPED_overlap*)lpOverlapped)->caller;
_this->_bytes_read = dwNumberOfBytesTransfered;
}
}
SetEvent(lpOverlapped->hEvent);
}
EDIT
Updated: I have spent most of the day on the theory that the OVERLAPPED variable went out of scope before the callback is executed. I have verified that this never happens and I have even tried to declare the OVERLAPPED struct as static, same problem remains. If the OVERLAPPED struct had gone out of scope, I would expect the callback to point at the memory location where the struct was previously allocated, but it doesn't, it points somewhere else, at an entirely unfamiliar memory location. Why it does that, I have no idea.
Maybe Windows 7/64 makes an internal hardcopy of the OVERLAPPED struct? I can see how that would cause this behavior, since I am relying on additional parameters sneaked in at the end of the struct (which seems like a hack to me, but apparently I got that "hack" from official MSDN examples).
I have also tried to change calling convention but this doesn't work at all, if I change it then the program crashes. (The standard calling convention causes it to crash, whatever standard is, cdecl? __fastcall also causes a crash.) The calling conventions that work are __stdcall, WINAPI and CALLBACK. I think these are all same names for __stdcall and I read somewhere that Win 64 ignores that calling convention anyhow.
It would seem that the callback is executed because of some "spurious disturbance" in Win 7/64 generating false callback calls with corrupt or irrelevant parameters.
Multi-thread race conditions is another theory, but in the scenario I am running to reproduce the bug, there is only one thread, and I can confirm that the thread calling ReadFileEx is the same one that is executing the callback.
I have found the problem, it turned out to be annoyingly simple.
In CreateFile(), I did not specify FILE_FLAG_OVERLAPPED. For reasons unknown, this was not necessary on 32-bit Windows. But if you forget it on 64-bit Windows, it will apparently still generate callbacks with the FileIOCompletionRoutine, but they have corrupted parameters.
I haven't found any documentation of this change of behavior anywhere; perhaps it was just an internal bug fix in Windows, since the older documentation also specifies that you must have FILE_FLAG_OVERLAPPED set.
As for my specific case, the bug appeared because I had a base class that assumed synchronous I/O, which has then been inherited by a class using asynchronous I/O.