When I run my program, it can run for a while, then all of the sudden, it experiences a huge memory leak. I traced it out using a snapshot of the heap when it crashed, and I have a mysterious char[] with the size of 232,023,801 Bytes. The minutes preceding crash have no unusual behavior until then. The only places where I use char arrays is in the following piece of code:
string ReadString(DWORD64 addr) {
char* buffer = new char[128];
bool validChar = true;
for (int c = 0; c < 128 && validChar; c++) {
buffer[c] = Mem.Read<char>(addr+ (0x1 * c), sizeof(char));
if (!isalnum(buffer[c]) && !ispunct(buffer[c]))
validChar = false;
}
string ret= string(buffer);
delete[] buffer;
return ret;
}
All this code should be doing is reading a few characters from memory, saving the char array to a string, cleaning up the array, and returning the string. How is the memory leak originating from here? Or does the char[] in the heap snapshot potentially point to another issue?
Assuming that string here is std::string:
You call string(buffer) which assumes that buffer is 0-terminated and allocates a new string. But your code doesn't ensure that buffer is actually 0-terminated, so this can cause undefined behavior, including potentially crashing or allocating too much memory for the string.
You probably want to use the string(buffer, size) constructor instead, which doesn't require buffer to be 0-terminated.
I'd also recommend avoiding the manual new/delete. One way to do this is to create an empty string and push_back the characters you read to it. This avoid the need for buffer.
Related
I am having an issue with a function that concatenates a number to the end of a char array. I honestly can't see the issue:
void x(int num, char* originalArray) {
char* concat_str = new char[1];
sprintf(concat_str, "%d", num);
for (int i = 0; i < 1; i++)
originalArray[i + 10] = concat_str[i];
delete [] concat_str;
}
The error message is:
HEAP CORRUPTION DETECTED: after Normal block (#147) at 0x01204CA0. CRT detected that the application wrote to the memory after the end of heap buffer.
Any ideas? I'm a beginning programmer, but I've done this same kind of thing many times and never had this issue. Thanks.
concat_str needs to be large enough to hold the number of digits in num plus a null terminator. Since it size is one you only have enough room for the null terminator. Trying to add anything else is undefined behavior as it accesses memory you do not own and causing the heap corruption.
You're allocating one byte for concat_str, and sprintf'ing something that requires more space than that.
First of all, note that your API is a C api. If you want a dynamic array in C++, use std::vector<char>.
But, assuming you need to stick to the C API, there's no way to guarantee that your originalArray is large enough to hold the result. Furthermore, the temporary buffer is unnecessary.
You should modify the API and the implementation as follows:
Take the size of the destination, to guarantee that it doesn't write past its end.
Write the string in place in the destination buffer. Putting it into a temporary buffer and then copying to the destination is a waste of time.
Use snprintf, not sprintf. The latter is not safe: you can't guarantee it won't write past the end of your buffer.
You should assert the precondition that the offset is at least smaller than the destination size.
If the precondition holds, the destination will be always properly zero-terminated, although the text representation of num might not fit in it.
You can return the length of the destination that is necessary for the value to fit.
Thus:
size_t fun(int num, char * dest, size_t dest_size) {
const size_t offset = 10;
assert(dest_size > offset);
return offset + snprintf(dest + offset, dest_size - offset, "%d", num);
}
My question arises from one of my c++ exercises (from Programming Abstraction in C++, 2012 version, Exercise 12.2). Here it is:
void strcpy(char *dst, char *src) {
while (*dst++ = *src++);
}
The definition of strcpy is dangerous. The danger stems from the fact
that strcpy fails to check that there is sufficient space in the
character array that receives the copy, thereby increasing the chance
of a buffer-overflow error. It is possible, however, to eliminate much
of the danger by using dynamic allocation to create memory space for
the copied string. Write a function
char *copyCString(char *str);
that allocates enough memory for the C-style string str and then
copies the characters—along with the terminating null character—into
the newly allocated memory.
Here's my question:
Is this new method really safe? Why it's safe?
I mean, to be a little bit radical, what if there isn't enough space in the heap?
Is the new operator able to check for space availability and fall in an elegant way if there isn't enough space?
Will that cause other kind of "something-overflow"?
If new fails to allocate the requested memory, it's supposed to throw a std::bad_alloc exception (but see below for more). After that, the stack will be unwound to the matching exception handler, and it'll be up to your code to figure out what to do from there.
If you really want/need to assure against an exception being thrown, there is a nothrow version of new you can use that will return a null pointer to signal failure--but this is included almost exclusively for C compatibility, and not frequently used (or useful).
For the type of situation cited in the question, you normally want to use std::string instead of messing with allocating space yourself at all.
Also note that on many modern systems, the notion of new either throwing or returning a null pointer in case of failure, is really fairly foreign. In reality, Windows will normally attempt to expand the paging file to meet your request. Linux has an "OOMKiller" process that will attempt to find "bad" processes and kill them to free up memory if you run out.
As such, even though the C++ standard (and the C standard) prescribe what should happen if allocation fails, that's rarely what happens in real life.
New operator will throw bad_alloc exception if it cannot alocate memory, unless nothrow specified. If you specify constant nothrow you will get NULL pointer back if it cannot alocate memory.
The code for strcpy is unsafe because it will try copying outside of the allocated memory for the dst pointer. Example:
int main()
{
const char* s1 = "hello"; // allocated space for 6 characters
char* s2 = new char[ 2 ]; // allocated space for 2 characters.
strcpy( s2, s1 );
cout << s2 << endl;
char c; cin >> c;
return 0;
}
This prints the correct value "hello", but remember that the pointer s2 was allocated to only have space for 2 characters. So we can assume that the other characters were written to the subsequent memory slots, which is unsafe as we could be overwriting data or accessing invalid memory.
Consider this solution:
char* e4_strdup( const char*& c )
{
// holds the number of space required for the c-string
unsigned int sz{ 0 };
// since c-style strings are terminated by the '\0' character,
// increase the required space until we've found a '\0' character.
for ( const char* p_to_c = c; *p_to_c != '\0'; ++p_to_c )
++sz;
// allocate correct amount of space for copy.
// we do ++sz during allocation because we must provide enough space for the '\0' character.
char* c_copy{ new char[ ++sz ] }; // extra space for '\0' character.
for ( unsigned int i{ 0 }; i < sz; ++i )
c_copy[ i ] = c[ i ]; // copy every character onto allocated memory
return c_copy;
}
The new operator will still return a std::bad_alloc exception if you run out of memory.
I was reviewing my skills with pointers and buffer in C++. I tried the code below and everything works fine. No leaks, no crash, nothing.
To be honest I didn't expect this.
When I call char* buf2 = new char[strlen(buf)] I didn't expect srlen(buf) returning the right size. I always thought that strlen
needs a NULL terminated string to work. Here it is not the case so why it is working this code?
int main(){
const char* mystr = "mineminemine";
char* buf = new char[strlen(mystr)];
memcpy(buf, mystr, strlen(mystr));
char* buf2 = new char[strlen(buf)];
memcpy(buf2, buf, strlen(buf));
delete[] buf2;
delete[] buf;
}
That's called undefined behavior - the program appears working but you can't rely on that.
When memory is allocated there happens a null character somewhere that is close enough to the start of the buffer and the program can technically access all memory between that null character and the start of the buffer so you don't observe a crash.
You can't rely on that behavior. Don't write code like that, always allocate enough space to store the terminating null character.
Consider another way to do the same thing:
int main(){
std::string mystr = "mineminemine";
std::string mystr2 = mystr;
}
Internally you have a buffer with a null terminating character added. When you copy a standard string you don't have to worry about keeping track of the start and end of the buffer.
Now considering the lifetime of the strings these two variables are declared on the stack and destroyed when main goes out of scope (e.g. terminationa). If you need strings to be shared amongst objects and you do not necessarily know when they will be destroyed I recommend considering using boost shared pointers.
I have to write a function that fills a char* buffer for an assigned length with the content of a string. If the string is too long, I just have to cut it. The buffer is not allocated by me but by the user of my function. I tried something like this:
int writebuff(char* buffer, int length){
string text="123456789012345";
memcpy(buffer, text.c_str(),length);
//buffer[length]='\0';
return 1;
}
int main(){
char* buffer = new char[10];
writebuff(buffer,10);
cout << "After: "<<buffer<<endl;
}
my question is about the terminator: should it be there or not? This function is used in a much wider code and sometimes it seems I get problems with strange characters when the string needs to be cut.
Any hints on the correct procedure to follow?
A C-style string must be terminated with a zero character '\0'.
In addition you have another problem with your code - it may try to copy from beyond the end of your source string. This is classic undefined behavior. It may look like it works, until the one time that the string is allocated at the end of a heap memory block and the copy goes off into a protected area of memory and fails spectacularly. You should copy only until the minimum of the length of the buffer or the length of the string.
P.S. For completeness here's a good version of your function. Thanks to Naveen for pointing out the off-by-one error in your terminating null. I've taken the liberty of using your return value to indicate the length of the returned string, or the number of characters required if the length passed in was <= 0.
int writebuff(char* buffer, int length)
{
string text="123456789012345";
if (length <= 0)
return text.size();
if (text.size() < length)
{
memcpy(buffer, text.c_str(), text.size()+1);
return text.size();
}
memcpy(buffer, text.c_str(), length-1);
buffer[length-1]='\0';
return length-1;
}
If you want to treat the buffer as a string you should NULL terminate it. For this you need to copy length-1 characters using memcpy and set the length-1 character as \0.
it seems you are using C++ - given that, the simplest approach is (assuming that NUL termination is required by the interface spec)
int writebuff(char* buffer, int length)
{
string text = "123456789012345";
std::fill_n(buffer, length, 0); // reset the entire buffer
// use the built-in copy method from std::string, it will decide what's best.
text.copy(buffer, length);
// only over-write the last character if source is greater than length
if (length < text.size())
buffer[length-1] = 0;
return 1; // eh?
}
char * Buffers must be null terminated unless you are explicitly passing out the length with it everywhere and saying so that the buffer is not null terminated.
Whether or not you should terminate the string with a \0 depends on the specification of your writebuff function. If what you have in buffer should be a valid C-style string after calling your function, you should terminate it with a \0.
Note, though, that c_str() will terminate with a \0 for you, so you could use text.size() + 1 as the size of the source string. Also note that if length is larger than the size of the string, you will copy further than what text provides with your current code (you can use min(length - 2, text.size() + 1/*trailing \0*/) to prevent that, and set buffer[length - 1] = 0 to cap it off).
The buffer allocated in main is leaked, btw
my question is about the terminator: should it be there or not?
Yes. It should be there. Otherwise how would you later know where the string ends? And how would cout would know? It would keep printing garbage till it encounters a garbage whose value happens to be \0. Your program might even crash.
As a sidenote, your program is leaking memory. It doesn't free the memory it allocates. But since you're exiting from the main(), it doesn't matter much; after all once the program ends, all the memory would go back to the OS, whether you deallocate it or not. But its good practice in general, if you don't forget deallocating memory (or any other resource ) yourself.
I agree with Necrolis that strncpy is the way to go, but it will not get the null terminator if the string is too long. You had the right idea in putting an explicit terminator, but as written your code puts it one past the end. (This is in C, since you seemed to be doing more C than C++?)
int writebuff(char* buffer, int length){
char* text="123456789012345";
strncpy(buffer, text, length);
buffer[length-1]='\0';
return 1;
}
It should most defiantly be there*, this prevents strings that are too long for the buffer from filling it completely and causing an overflow later on when its accessed. though imo, strncpy should be used instead of memcpy, but you'll still have to null terminate it. (also your example leaks memory).
*if you're ever in doubt, go the safest route!
First, I don't know whether writerbuff should terminate the string or not. That is a design question, to be answered by the person who decided that writebuff should exist at all.
Second, taking your specific example as a whole, there are two problems. One is that you pass an unterminated string to operator<<(ostream, char*). Second is the commented-out line writes beyond the end of the indicated buffer. Both of these invoke undefined behavior.
(Third is a design flaw -- can you know that length is always less than the length of text?)
Try this:
int writebuff(char* buffer, int length){
string text="123456789012345";
memcpy(buffer, text.c_str(),length);
buffer[length-1]='\0';
return 1;
}
int main(){
char* buffer = new char[10];
writebuff(buffer,10);
cout << "After: "<<buffer<<endl;
}
In main(), you should delete the buffer you allocated with new., or allocate it statically (char buf[10]). Yes, it's only 10 bytes, and yes, it's a memory "pool," not a leak, since it's a one-time allocations, and yes, you need that memory around for the entire running time of the program. But it's still a good habit to be into.
In C/C++ the general contract with character buffers is that they be null-terminiated, so I would include it unless I had been explicitly told not to do it. And if I did, I would comment it, and maybe even use a typedef or name on the char * parameter indicating that the result is a string that is not null terminated.
All I need this for is strcpy().
I want to see whether the first three bytes of a buffer(byte array) are "JMX" as string.
This is what I did so far:
char * ddj;
strcpy( ddj, buffer ); //buffer is BYTE[]
if ( strcmp( "JMX", ddj ) == 0 ) //check first three chars are "JMX"
{
buffer += 20; //increase the index with 20
size -= 20; //int
}
I get exception at strcmp() line. What is the problem?
I wish I was writing this in C# :(
Tho things go wrong here:
ddj does not point to any actual memory. Hence the copy will have undefined behavior
The copying is not necessary in the first place.
This is what you can do:
if(strncmp("JMX", buffer, 3) == 0) {
buffer += 20;
size -= 20;
}
This uses strncmp instead of strcmp, thus ensuring that no more than three bytes are compared. If buffer can contain less than three bytes, you should do something like:
if(buf_len >= 3 && strncmp("JMX", buffer, 3) == 0) {
buffer += 20;
size -= 20;
}
You're not allocating any memory for ddj. Since it's a local variable, it's allocated on the stack. Local variables are not initialized to 0/false/NULL by default, so the value of ddj immediately after it's declared is undefined -- it will have the value of whatever is left in memory at that particular location on the stack. Any attempt to dereference it (that is, to read or write the memory at which it's pointing) will have undefined behavior. In your case, it's crashing because it's pointing to an invalid address.
To fix the problem, you need to allocate storage for ddj. You can either allocate static storage on the stack, or dynamic storage on the heap. To allocate static storage, do:
// Allocate 64 bytes for ddj. It will automatically be deallocated when the function
// returns. Be careful of buffer overflows!
char ddj[64];
To allocate dynamic storage:
// Allocate 64 bytes for ddj. It will NOT be automatically deallocated -- you must
// explicitly deallocate it yourself at some point in the future when you're done
// with it. Be careful of buffer overflows!
char *ddj = new char[64];
...
delete [] ddj; // Deallocate it
Instead of managing storage yourself, it would be a better idea to use std::string, which automatically deals with memory management.
Finally, since all you're doing is comparing the first three characters of the string, there's no need to jump through hoop to copy the string and compare it. Just use strncmp():
if(strncmp(buffer, "JMX", 3) == 0)
{
...
}
You have not allocated memory for ddj. Allocate memory using new to it . For example
char *ddj = new char[size]; //Allocate size number of chars
//do the required comaprisons
delete[] ddj; //Remember to release the memory.
On the other hand you can use std::string which is a standard string class.
You must allocate new memory for ddj. Either declare it as
char ddj[NAX_LENGTH];
or with dynamic allocation
char* ddj = new char[length]; // You must use delete[] to free the memory in the end.
A more convenient alternative is std::string.
Firstly, it's crashing because ddj doesn't point to anything.
Secondly, you don't need to copy the data from byte[] to char* (they're essentially the same thing). You can just do:
if (strncmp("JMX", reinterpret_cast<char*>(buffer), 3) == 0)
{
// Strings are equal, do what you want
}
This is UB because ddj isn't pointing to anything. You need to allocate memory:
char* ddj = new char[strlen(buffer) + 1];
Be sure to delete the memory you allocated using delete[] (not plain delete!).
You could also use std::string which is generally safe, as you don't have to deal with pointers and memory alloation.
Looking at your code, however, ddj seems useless. Just use buffer:
if ( strcmp( "JMX", buffer ) == 0 ) //check first three chars are "JMX"
{
buffer += 20; //increase the index with 20
size -= 20; //int
}
You are getting the exception because the variable 'ddj' isn't initialized. It is pointing at garbage, so who knows where you are copying that string...
You don't really need to copy the bytes before comparing them, though.
if(strncmp("JMX", buffer, 3) == 0) // check if the first three characters are "JMX"
{
buffer += 20;
size -= 20;
}
if you want to strcmp ddj, you can also do it on buffer first, and make a copy of buffer if you need it later.