This is a printing thread that prints the statistic of my currently running program
void StatThread::PrintStat(){
clock_t now = 0;
UINT64 oneMega = 1<<20;
const char* CUnique = 0;;
const char* CInserted = 0;;
while((BytesInserted<=fileSize.QuadPart)&&flag){
Sleep(1000);
now = clock();
CUnique = FormatNumber(nUnique);
CInserted = FormatNumber(nInserted);
printf("[ %.2f%%] %u / %u dup %.2f%% @ %.2fM/s %.2fMB/s %3.2f%% %uMB\n",
(double)BytesInserted*100/(fileSize.QuadPart),
nUnique,nInserted,(nInserted-nUnique)*100/(double)nInserted,
((double)nInserted/1000000)/((now - start)/(double)CLOCKS_PER_SEC),
((double)BytesInserted/oneMega)/((now - start)/(double)CLOCKS_PER_SEC),
cpu.GetCpuUtilization(NULL),cpu.GetProcessRAMUsage (true));
if(BytesInserted==fileSize.QuadPart)
flag=false;
}
delete[] CUnique; //would have worked with memory leak if commented out
delete[] CInserted; // crash at here! heap corruption
}
This is FormatNumber that returns a pointer to a char array
const char* StatThread::FormatNumber(const UINT64& number) const{
char* result = new char[100];
result[0]='\0';
_i64toa_s(number,result,100,10);
DWORD nDigits = ceil(log10((double)number));
result[nDigits] = '\0';
if(nDigits>3){
DWORD nComma=0;
if(nDigits%3==0)
nComma = (nDigits/3) -1;
else
nComma = nDigits/3;
char* newResult = new char[nComma+nDigits+1];
newResult[nComma+nDigits]='\0';
for(DWORD i=1;i<=nComma+1;i++){
memcpy(newResult+strlen(newResult)-i*3-(i-1),result+strlen(result)-i*3,3);
if(i!=nComma+1){
*(newResult+strlen(newResult)-4*i) = ',';
}
}
delete[] result;
return newResult;
}
return result;
}
What is really weird was that it crashed only in release mode because of a heap corruption, but run smoothly in debug mode. I’ve already checked everywhere and found no obvious memory leaks, and even Memory Leak Detector said so too.
Visual Leak Detector Version 2.2.3 installed.
The thread 0x958 has exited with code 0 (0x0).
No memory leaks detected.
Visual Leak Detector is now exiting.
The program '[5232] Caching.exe' has exited with code 0 (0x0).
However, when run in release mode,it threw an error that said my program stop working and I clicked on debug, it pointed to the line that caused the heap corruption.
The thread 0xe4c has exited with code 0 (0x0).
Unhandled exception at 0x00000000770E6AE2 (ntdll.dll) in Caching.exe: 0xC0000374: A heap has been corrupted (parameters: 0x000000007715D430).
If I commented out this line, it worked fine but Memory Leak Detector would have complained about memory leak! I don’t understand how to cause a heap corruption when there was no memory leaks, (at least that’s what the Leak Detector said). Please help, Thank you in advance.
Edit:
Heap corruption was fixed, because in the very last iteration, I still copied 3 byes to the front instead of whatever is leftover. Thank you all for helps!
const char* StatThread::FormatNumber(const UINT64& number) const{
char* result = new char[100];
result[0]='\0';
_ui64toa_s(number,result,100,10);
DWORD nDigits = (DWORD)ceil(log10((double)number));
if(number%10==0){
nDigits++;
}
result[nDigits] = '\0';
if(nDigits>3){
DWORD nComma=0;
if(nDigits%3==0)
nComma = (nDigits/3) -1;
else
nComma = nDigits/3;
char* newResult = new char[nComma+nDigits+1];
DWORD lenNewResult = nComma+nDigits;
DWORD lenResult = nDigits;
for(DWORD i=1;i<=nComma+1;i++){
if(i!=nComma+1){
memcpy(newResult+lenNewResult-4*i+1,result+lenResult-3*i,3);
*(newResult+lenNewResult-4*i) = ',';
}
else{
memcpy(newResult,result,lenNewResult-4*(i-1));
}
}
newResult[nComma+nDigits] = '\0';
delete[] result;
return newResult;
}
return result;
}
Sorry to be blunt, but the code to “format” a string is horrible.
First of all, you pass in an unsigned 64-bit int value, which you formatted as a signed value instead. If you claim to sell bananas, you shouldn’t give your customers plantains instead.
But what’s worse is that what you do return (when you don’t crash) isn’t even right. If a user passes in 0, well, then you return nothing at all. And if a user passes in 1000000 you return 100,000 and if he passes in 10000000 you return 1,000,000. Oh well, what’s a factor of 10 for some numbers between friends? 😉
These, along with the crash, are symptoms of the crazy pointer arithmetic your code does. Now, to the bugs:
First of all, when you allocate ‘newResult’ you leave the buffer in a very weird state. The first nComma + nDigits bytes are random values, followed by a NULL. You then call strlen on that buffer. The result of that strlen can be any number between 0 and nComma + nDigits, because any one of the nComma + nDigit characters may contain the null byte, which will cause strlen to terminate prematurely. In other words, the code is non-deterministic after that point.
Sidenote: If you’re curious why it works in debug builds, it’s because the compiler and the debug version of the runtime libraries try to help you catch bugs by initializing memory for you. In Visual C++ the fill mask is usually 0xCC. This made sure that the bug in your strlen() was covered up in debug builds.
Fixing that bug is pretty simple: simply initialize the buffer with spaces, followed by a NULL.
But there’s one more bug. Let’s try to format the number 1152921504606846975 which should become 1,152,921,504,606,846,975. Let’s see what some of fancy pointer arithmetic operations give us:
As you can see, your very last operation copies data 2 bytes before the beginning of the buffer you allocated. This is because you assume that you will always be copying 3 characters. Of course, that’s not always the case.
Frankly, I don’t think your version of FormatNumber should be fixed. all that pointer arithmetic and calculations are bugs waiting to happen. Here’s the version I wrote, which you can use if you want. I consider it much more sane, but your mileage may vary:
P.S.: The “off by a factor of 10” thing is left as an exerise to the reader.