I have a big problem with std::wstring memory allocation. The program crash when I try to use this code:
size_t size;
mbstowcs_s(&size, NULL, 0, buffer, _TRUNCATE);
wchar_t *buffer2 = (wchar_t*)malloc(size + 1);
mbstowcs_s(&size, buffer, buffer_size, buffer, _TRUNCATE);
buffer2[size] = '\0';
std::wstring data(buffer);
the crash is on the last line and doesn't happen if I use the following line:
std::wstring data(L"hello");
the error is memory heap allocation failure and the result is the crash of the program. Why? What's wrong?
wchar_t *buffer2 = (wchar_t*)malloc((size + 1) * sizeof(wchar_t));
^^^^^^^^^^^^^^^^^
malloc allocates a number of bytes - you wan't a number of wchar_t's
If you're using c++, the correct way is:
wchar_t *buffer2 = new wchar_t[size+1];
If you use std::wstring I assume you are using C++, dont use malloc, use new & delete (just a side note)
std::vector seems to be a good way to make a buffer here. Its constructor receives elements number (not bytes) and you don't have to remember to delete the memory.
Related
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);
Can anyone help me understand why my code is failing on delete[] szPassword in the first block of code? I know that szPassword is only copying "a", and st2 is equal to 8:
TCHAR *szPassword = new TCHAR[2]();
StringCchCopy(szPassword, 2, L"ab");
SIZE_T st2 = sizeof(szPassword);
SecureZeroMemory(szPassword, st2);
delete[] szPassword;
However, when this is run, without getting the sizeof() value, it works fine:
TCHAR *szPassword = new TCHAR[2]();
StringCchCopy(szPassword, 2, L"ab");
SecureZeroMemory(szPassword, 2);
delete[] szPassword;
szPassword is a pointer, not an array and therefore sizeof(szPassword) will be 4 or 8. In a 64-bit application this is too much, you will attempt to write 8 bytes to a 4 byte buffer.
The C++ run-time is allowed to allocate more than you ask it to and it often does this so it can add special data to the end of the buffer so it can detect buffer overruns.
Do something like this instead:
const UINT charcount = 2;
TCHAR *szPassword = new TCHAR[charcount];
...
SecureZeroMemory(szPassword, charcount * sizeof(TCHAR));
delete[] szPassword;
If the buffer is always small-ish you can just use an array on the stack:
TCHAR szPassword[200];
...
SecureZeroMemory(szPassword, sizeof(szPassword));
I know this kind of question has already been asked. I also used the solution of this topic to my tests. However, I want to know how using this kind of function without memory leak neither exception.
Note:
LPTSTR ~ char* and
LPCTSTR ~ const char*
void add_to_buffer(LPTSTR* buffer, LPCTSTR msg) {
// Determine new size
int newSize = 0;
// Allocate new buffer
if (*buffer == NULL)
newSize = _tcsclen(msg) + 1; // strlen()
else
newSize = _tcslen(*buffer) + _tcsclen(msg) + 1;
LPTSTR newBuffer = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, newSize); // malloc()
// Do the copy and concat
if (*buffer == NULL)
_tcscpy(newBuffer, msg); // strcpy()
else
{
_tcscpy(newBuffer, *buffer);
_tcscat(newBuffer, msg); // strcat()
// release old buffer
HeapFree(GetProcessHeap(), 0, *buffer); // free()
}
// store new pointer
*buffer = newBuffer;
}
Tests:
LPTSTR test = NULL;
add_to_buffer(&test, _T("User:\r\n"));
add_to_buffer(&test, _T("42"));
First call to add_to_buffer works. However, the second function call causes an exception at HeapFree. I'm sure this is a problem about pointers, but I do not understand how to fix it.
Is it a good method? How to fix my exception?
If you are compiling the code as multi-byte application this line
LPTSTR newBuffer = (LPTSTR)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
newSize
);
might allocate to few memory.
To fix this use:
LPTSTR newBuffer = (LPTSTR)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
newSize * sizeof(*newBuffer)
);
Besides this and the fact that the code lacks error checking for system calls the code looks fine.
However, to simplify things one can use HeapReAlloc() instead of the combination of HeapAlloc() and HeapFree().
If the program crashes anyway this might due to the memory management already being mashed up before this actual crash you observe.
If your program is unicode-enabled, you're not allocating enough memory - because string length (in symbols) and string size (in bytes) don't match.
If it isn't, I don't see reason of using non-standard-C types over standard ones. It shouldn't be a problem though.
Visual Studio 2010
When I allocate memory for a char string, pass the string (pointer) to a function, then try to free the memory, I get a "Heap Corruption Detected" run-time error.
I suspect this is a result of the function marking the memory as "freed" once it returns, but I am still puzzled as how to get around this. I do not feel comfortable simply removing the call to free().
// this function takes a char string, converts it to a wide char string,
// then passes the converted string to another function.
void Class::WriteToLog(const char * msg)
{
// allocate memory
wchar_t * wMsg = (wchar_t*)malloc((strlen(msg) * sizeof(wchar_t)) + 1); // add one for null terminating char
// convert to wide char. This works fine, and returns 4 when msg is 4 chars long.
numChar = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, msg, strlen(msg), wMsg, strlen(msg));
wMsg[numChar] = L'\0'; // terminate string
// At this point wMsg contains the same thing msg contains. Works fine.
WriteToLogW(wMsg); // Works fine. Writes the string to a text file.
free(wMsg); // Heap Corruption Detected
}
void Class::WriteToLogW(const wchar_t * msg)
{
...
}
The +1 you added at the end of your malloc will only allocate enough memory for the null terminator when working with chars, as chars are 1 byte (and hence the null terminator will take up one byte).
Since you're using wchar_t, the null terminator will take up sizeof(wchar_t) bytes, which will virtually always be greater than 1. Therefore, the malloc call allocates insufficient memory. Re-structure your malloc call to look like this:
wchar_t * wMsg = (wchar_t*) malloc((strlen(msg) + 1) * (sizeof(wchar_t));
This way the total number of bytes allocated leaves room for the null terminator.
Even the terminating nul of a wide-character string is wide, so giving it only a single byte isn't enough. Change:
wchar_t * wMsg = (wchar_t*)malloc((strlen(msg) * sizeof(wchar_t)) + 1);
to
wchar_t * wMsg = (wchar_t*)malloc((strlen(msg) + 1) * sizeof(wchar_t));
According to the Visual C++ runtime there is a heap corruption when calling free in the destructor. But I don't understand why there is a heap corruption, can anyone explain why? The precise error is:
CRT detected that the application wrote to memory after end of heap buffer.
Also, if I ignore the error the program doesn't crash, it keeps running and when I press a key it returns 0.
The class only contains a constructor and destructor and the private vars FILE* target and char* raw_data.
foo::foo (wchar_t* path)
{
size_t size;
target = _wfopen (path, L"rb+");
if (!target) {
char* error = strerror (errno);
printf ("The file could not be opened: %s\n", error);
_exit (1);
}
fseek (target, 0L, SEEK_END);
size = ftell (target);
fseek (target, 0, SEEK_SET);
raw_data = (char*) malloc (size);
size = fread (raw_data, 1, size, target);
raw_data[size] = '\0';
}
foo::~foo ()
{
fclose (target);
free (raw_data);
}
int main ()
{
nbt* klas = new nbt (L"C:\\Users\\Ruben\\level");
puts ("Success?!");
delete klas;
getchar ();
return 0;
}
When writing the NUL terminator as you do:
raw_data[size] = '\0';
... you are using one byte more than the bytes you allocated. There may be other errors but there is definitely an error on this line -- writing to memory you have not allocated is "undefined behaviour" and could explain the crash you're observing.
One sure problem is this code:
raw_data = (char*) malloc (size);
size = fread (raw_data, 1, size, target);
raw_data[size] = '\0';
You cannot access raw_data[size], because it is beyond the allocated size. Indexed access in C/C++ is zero based. As a result, the last element of raw_data that can be accessed with your existing code is raw_data[size-1]. To be able to set the byte which is at offset size to zero you need to change your malloc to:
raw_data = (char*) malloc (size+1);
Since this is a C++ application, you may want to use streams and new/delete instead of FILE pointers and malloc/free.