What causes the heap corruption in my method? - c++

So I have tracked down an annoying heap corruption to a single method.
DWORD gdwCounter = 0;
TCHAR* GetName(const TCHAR* format, size_t len)
{
len += (snprintf(NULL, 0, "%lu", gdwCounter) * sizeof(TCHAR));
TCHAR *c = (TCHAR*)malloc(len);
_stprintf_s(c, len, __TEXT("%s%lu"), format, gdwCounter);
return c;
}
To make sure I found the correct method, I tried to change it and just copy the 'format' buffer it gets passed as an parameter to the output buffer. Heap corruption went away and everything was fine again.
I decided to look at the documentations of snprintf and _stprintf_s.
snprintf is supposed to return the required characters without the null-terminating character to actually print your buffer in a second call to it.
My len parameter already contains the full size (with null-terminating character) of format.
Also I couldn't find any hints to what is wrong in the documentation of _stprintf_s.
So what am I missing?
Edit: After further testing I found out that apparently _stprintf_s causes the error as snprintf does return the correct size.

TCHAR* GetName(const TCHAR* format, size_t len)
{
len += snprintf(NULL, 0, "%lu", gdwCounter);
TCHAR *c = (TCHAR*)malloc(len*sizeof(TCHAR));
_stprintf_s(c, len, __TEXT("%s%lu"), format, gdwCounter);
return c;
}
_stprintf_s takes the "Maximum number of characters to store" instead of maximum number of bytes.

Related

wchar_t array size issue with wcscpy_s

I'm confused with a size issue. Running the following code throws an exception at runtime. Specifically it seems to appear at end, and the text still be pasted with success. Due to my limited skills I'm not able to interpret the exception clearly.
It started when I decided to use the wcscpy_s function due to depreciation of wcscpy which worked fine in my noob program.
#define _CRT_SECURE_NO_WARNINGS
#include <afxwin.h>
int main() {
wchar_t wcSource[7] = L"Testeu"; // Throws an exception error. However, wcSource[8] doesn't
//wchar_t wcSource[9] = L"TestCopy"; // Runs fine
UINT iSize = sizeof(wcSource);
if (OpenClipboard(NULL)) {
EmptyClipboard();
HGLOBAL hClipboardData;
hClipboardData = GlobalAlloc(GMEM_DDESHARE, iSize);
wchar_t *wpchData;
wpchData = (wchar_t*)GlobalLock(hClipboardData);
//wcscpy(wpchData, wcSource); // Works fine
wcscpy_s(wpchData, iSize, wcSource);
GlobalUnlock(hClipboardData);
SetClipboardData(CF_UNICODETEXT, hClipboardData);
CloseClipboard();
}
return 0;
}
wcscpy_s() expects a CHARACTER count, but you are passing it a BYTE count instead. On Windows, sizeof(wchar_t) is 2 bytes.
You need a BYTE count when allocating memory for the clipboard buffer (which in your example will require 14 bytes), but since you are passing the BYTE count as a CHARACTER count to wcscpy_s(), you are telling it that the clipboard buffer can hold up to 14 wchar_t elements, when in actuality it can hold only 7. You are giving wcscpy_s() permission to go out of bounds of the clipboard buffer (for instance, if it wants to pre-fill the buffer memory before then filling it with actual characters). Doing so would corrupt the call stack, which could easily cause an exception when main() exits.
You need to pass wcscpy_s() the max number of CHARACTERS that the clipboard buffer can hold. Not the max number of BYTES it can hold.
You can do that by dividing iSize by sizeof(wchar_t), eg:
wcscpy_s(wpchData, iSize / sizeof(wchar_t), wcSource);
Alternatively, since you are using the exact BYTE size of the source array to allocate the clipboard buffer, you can use _countof() to get the number of CHARACTERS in the array (you cannot pass the allocated clipboard buffer to _countof()), eg:
wcscpy_s(wpchData, _countof(wcSource), wcSource);
Alternatively, you can use wsclen() instead, eg:
wchar_t wcSource[] = L"Testeu";
int iLen = wcslen(wcSource) + 1;
UINT iSize = iLen * sizeof(wchar_t);
...
hClipboardData = GlobalAlloc(GMEM_DDESHARE, iSize);
...
wcscpy_s(wpchData, iLen, wcSource);

String is not null terminated error

I'm having a string is not null terminated error, though I'm not entirely sure why. The usage of std::string in the second part of the code is one of my attempt to fix this problem, although it still doesn't work.
My initial codes was just using the buffer and copy everything into client_id[]. The error than occurred. If the error is correct, that means I've got either client_ id OR theBuffer does not have a null terminator. I'm pretty sure client_id is fine, since I can see it in debug mode. Strange thing is buffer also has a null terminator. No idea what is wrong.
char * next_token1 = NULL;
char * theWholeMessage = &(inStream[3]);
theTarget = strtok_s(theWholeMessage, " ",&next_token1);
sendTalkPackets(next_token1, sizeof(next_token1) + 1, id_clientUse, (unsigned int)std::stoi(theTarget));
Inside sendTalkPackets is. I'm getting a string is not null terminated at the last line.
void ServerGame::sendTalkPackets(char * buffer, unsigned int buffersize, unsigned int theSender, unsigned int theReceiver)
{
std::string theMessage(buffer);
theMessage += "0";
const unsigned int packet_size = sizeof(Packet);
char packet_data[packet_size];
Packet packet;
packet.packet_type = TALK;
char client_id[MAX_MESSAGE_SIZE];
char theBuffer[MAX_MESSAGE_SIZE];
strcpy_s(theBuffer, theMessage.c_str());
//Quick hot fix for error "string not null terminated"
const char * test = theMessage.c_str();
sprintf_s(client_id, "User %s whispered: ", Usernames.find(theSender)->second.c_str());
printf("This is it %s ", buffer);
strcat_s(client_id, buffersize , theBuffer);
Methinks that problem lies in this line:
sendTalkPackets(next_token1, sizeof(next_token1) + 1, id_clientUse, (unsigned int)std::stoi(theTarget));
sizeof(next_token1)+1 will always gives 5 (on 32 bit platform) because it return size of pointer not size of char array.
One thing which could be causing this (or other problems): As
buffersize, you pass sizeof(next_token1) + 1. next_token1 is
a pointer, which will have a constant size of (typically) 4 or 8. You
almost certainly want strlen(next_token1) + 1. (Or maybe without the
+ 1; conventions for passing sizes like this generally only include
the '\0' if it is an output buffer. There are a couple of other
places where you're using sizeof, which may have similar problems.
But it would probably be better to redo the whole logic to use
std::string everywhere, rather than all of these C routines. No
worries about buffer sizes and '\0' terminators. (For protocol
buffers, I've also found std::vector<char> or std::vector<unsigned char>
quite useful. This was before the memory in std::string was
guaranteed to be contiguous, but even today, it seems to correspond more
closely to the abstraction I'm dealing with.)
You can't just do
std::string theMessage(buffer);
theMessage += "0";
This fails on two fronts:
The std::string constructor doesn't know where buffer ends, if buffer is not 0-terminated. So theMessage will potentially be garbage and include random stuff until some zero byte was found in the memory beyond the buffer.
Appending string "0" to theMessage doesn't help. What you want is to put a zero byte somewhere, not value 0x30 (which is the ascii code for displaying a zero).
The right way to approach this, is to poke a literal zero byte buffersize slots beyond the start of the buffer. You can't do that in buffer itself, because buffer may not be large enough to accomodate that extra zero byte. A possibility is:
char *newbuffer = malloc(buffersize + 1);
strncpy(newbuffer, buffer, buffersize);
newbuffer[buffersize] = 0; // literal zero value
Or you can construct a std::string, whichever you prefer.

GetComputerName() works on win7 but fails on xp

I am trying to use GetComputerName() to retrieve the box's name. The code works great on windows 7, however, when i test on windows xp the code does not work (result is UNKNOWN). Any idea why?
int GetBoxName(BoxInfo &box_info)
{
int Ret;
DWORD dwLen;
const char* szUnk = "UNKNOWN\0";
// Get the size of the box name then allocate memory.
Ret = GetComputerName(NULL, &dwLen);
box_info.BoxName = new char[dwLen];
// Attempt to retrieve the box name.
if((Ret = GetComputerName(box_info.BoxName, &dwLen) == 0))
{
delete[] box_info.BoxName;
box_info.BoxName = new char[strlen(szUnk)];
box_info.BoxName = (char*)szUnk;
return 1;
}
return 0;
}
#Ben has given a good account of errors that you made. I'd like to show you how you would typically call GetComputerName. You are making it harder than it needs to be. The key information is the excerpt from the documentation:
The buffer size should be large enough to contain MAX_COMPUTERNAME_LENGTH + 1 characters.
You have a hard upper bound on the size of the buffer. You can therefore use a fixed size buffer, and only make a single call to GetComputerName.
std::string getComputerName()
{
char buffer[MAX_COMPUTERNAME_LENGTH + 1];
DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
if (GetComputerName(buffer, &len))
return std::string(buffer, len);
return "UNKNOWN";
}
Were you compiling for Unicode it would be:
std::wstring getComputerName()
{
wchar_t buffer[MAX_COMPUTERNAME_LENGTH + 1];
DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
if (GetComputerName(buffer, &len))
return std::wstring(buffer, len);
return L"UNKNOWN";
}
If you want to cater for the possibility of the computer name being longer than MAX_COMPUTERNAME_LENGTH then you can write it like this:
std::string getComputerName()
{
char buffer[MAX_COMPUTERNAME_LENGTH + 1];
DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
if (GetComputerName(buffer, &len))
{
return std::string(buffer, len);
}
if (GetLastError() == ERROR_BUFFER_OVERFLOW)
{
std::vector<char> name(len);
if (GetComputerName(&name[0], &len))
{
return std::string(&name[0], len);
}
}
return "UNKNOWN";
}
I don't know whether or not this can even happen. The docs hint that it can, although if it does happen then it renders MAX_COMPUTERNAME_LENGTH somewhat of a misnomer. If we pass a value of len that is less than MAX_COMPUTERNAME_LENGTH+1 then the function succeeds if the name fits. It does not automatically fail with ERROR_BUFFER_OVERFLOW. Of course, if the name returned by this function can never exceed MAX_COMPUTERNAME_LENGTH then the second version is rather paranoid.
FWIW, the code in your updated answer is still badly broken. You simply must not pass NULL for the first parameter to GetComputerName. The documentation could not be much clearer.
This makes no sense at all:
box_info.BoxName = new char[strlen(szUnk)];
box_info.BoxName = (char*)szUnk;
You allocate memory, then immediately lose track of it. And you are directing a non-const pointer to a string literal. And the amount of memory allocated here doesn't include space for the terminating NUL byte, so you would overrun the buffer.
Perhaps you wanted the second line to be
strcpy(box_info.BoxName, szUnk);
And why aren't you using a smart pointer to automatically deallocate the memory when needed, for example std::string or std::unique_ptr<char[]> ?
Finally, the documentation says
The buffer size should be large enough to contain MAX_COMPUTERNAME_LENGTH + 1 characters.
That is a pretty plain requirement. Makes you wonder the point of
If the buffer is too small, the function fails and GetLastError returns ERROR_BUFFER_OVERFLOW. The lpnSize parameter specifies the size of the buffer required, including the terminating null character.
This latter behavior appears to only be true for the Unicode version. Stanly Roark left a comment on the MSDN page that:
The ANSI version does not return the required length
I have to maintain an old application that uses ANSI.
I have noticed that, while the Unicode version returns the required buffer length, the ANSI version does not.

How to declare appropriate size for the buffer

I'm using TCHAR in the Visual C++ poject I'm working on, which definition is shown below:
#ifdef _UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif
I need to put some data into buffer buff:
char buff[size] = {0}; // how to declare the buffer size - what should be its value ?
sprintf(buff, "%s (ID: %i)", res->name(), res->id());
where:
name() returns TCHAR*
id() returns int
How to calculate the value of size - exact buffer capacity for actual needs (smaller if no unicode is defined, bigger if unicode is defined) ? In addition I'd like to protect myself from buffer overflow possibility, what kind of protection should I use ?
What's more, I've declared here the buffer as char. If I declare the buffer as int, would it be any difference for the size value (i.e 4 times smaller if compared to declared as char) ?
UPDATE
What I come up with partially based on Mats Petersson answer is:
size_t len;
const char *FORMAT;
#ifndef _UNICODE
len = strlen((char*)res->name());
FORMAT = "%s (ID: %i)";
#else
len = wcslen(res->name());
FORMAT = "%S (ID: %i)";
#endif
int size = 7 * sizeof(TCHAR) + /* place for characters inside format string */
len * sizeof(TCHAR) + /* place for "name" characters */
strlen(_itoa(id, ioatmp, 10)) * sizeof(TCHAR) + /* place for "id" digits */
1 * sizeof(TCHAR); /* zero byte(s) string terminator */
char *buff = new char[size]; /* buffer has to be declared dynamically on the heap,
* because its exact size is not known at compilation time */
sprintf(buff, FORMAT, name, id);
delete[] buff;
Is it correct thinking or did I miss something ?
To begin from the back, buff should always be char, because that's what is being stored by sprintf.
Second, if your res->name() is returning a wide-char (unicode) string, your format string should use "%S", for regular ASCII you should use "%s".
Now, to calculate the length required for the buffer, and avoid overflows. It's not that hard to do something like
const TCHAR *nm = res->name();
size_t len;
#ifndef UNICODE
len = strlen(nm);
#else
... see below.
#endif
and then guesstimate the length of the number (an integer can't take more than 12 places), along with the exact number of characters produced as constants in the format string.
This works fine for the standard ASCII variant.
However, it gets more fun with the wide char variant, as that can take up multiple bytes in the output string (e.g. writing Chinese characters that always require multibyte encoding). One solution is:
len = snprintf(0, NULL, "%S", nm);
which should give you the correct number [I think]. It's a pretty cumbersome method, but it will work. I'm not sure there is an easy way to convert a wide-string to "number of bytes needed to store this string" in another way.
Edit: I would seriously consider if it's much point in supporting non-UNICOD veariant, and then just convert the whole thing to using swprintf(...) instead. You still need the length, but it should just be the result of of wcslen(res->name()), rather than requiring some complex conversion calculation.
you can use: snprintf / swnprintf, it will return you number of chars/wchars needed.
here char buff[size] = {0}; you are writing outside of the buffer. UPDATE: I'll take that back - it just a declaration with initialization if size is constant.
this "%s (ID: %i)" shall be changed to this: "%s (ID: %d)" if last parameter is int.

How do you convert from a nsACString to a LPCWSTR?

I'm making a firefox extension (nsACString is from mozilla) but LoadLibrary expects a LPCWSTR. I googled a few options but nothing worked. Sort of out of my depth with strings so any references would also be appreciated.
It depends whether your nsACString (which I'll call str) holds ASCII or UTF-8 data:
ASCII
std::vector<WCHAR> wide(str.Length()+1);
std::copy(str.beginReading(), str.endReading(), wide.begin());
// I don't know whether nsACString has a terminating NUL, best to be sure
wide[str.Length()] = 0;
LPCWSTR newstr = &wide[0];
UTF-8
// get length, including nul terminator
int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
str.BeginReading(), str.Length(), 0, 0);
if (len == 0) panic(); // happens if input data is invalid UTF-8
// allocate enough space
std::vector<WCHAR> wide(len);
// convert string
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
str.BeginReading(), str.Length(), &wide[0], len)
LPCWSTR newstr = &wide[0];
This allocates only as much space as is needed - if you want faster code that potentially uses more memory than necessary, you can replace the first two lines with:
int len = str.Length() + 1;
This works because a conversion from UTF-8 to WCHAR never results in more characters than there were bytes of input.
Firstly note: LoadLibrary need not accept a LPWSTR. Only LoadLibraryW does. You may call LoadLibraryA directly (passing a narrow LPCSTR) and it will perform the translation for you.
If you choose to do it yourself however, below is one possible example.
nsACString sFoo = ...; // Some string.
size_t len = sFoo.Length() + 1;
WCHAR *swFoo = new WCHAR[len];
MultiByteToWideChar(CP_ACP, 0, sFoo.BeginReading(), len - 1, swFoo, len);
swFoo[len - 1] = 0; // Null-terminate it.
...
delete [] swFoo;
nsACString a;
const char* pData;
PRUint32 iLen = NS_CStringGetData(a, &pData);