I have an object with the following method:
int PathSubstitution::updateField(Field4memo &field, int record_id, int field_id) const
{
int type = field.type();
if((type == r4str) || (type == r4memo) || (type == r4unicode))
{
string value = field.str();
trim(value);
if(!substituteDriveLetters(value))
return -1;
if(!substituteGridMount(value))
return -1;
return field.assign(value.c_str(), value.length());
}
return r4success;
}
When I build this code with my Debug profile in Visual Studio C++ 2010 everything works just fine. This method gets called 4 times, on four unique Field4memo objects, and it works.
When I build this code with my Release profile the method works the first time it's called, but causes Vista Enterprise to display a "program.exe has stopped working" dialog window. The "View problem details" area of the window says:
Problem signature:
Problem Event Name: BEX
Application Name: program.exe
Application Version: 0.0.0.0
Application Timestamp: 4ef4edc6
Fault Module Name: program.exe
Fault Module Version: 0.0.0.0
Fault Module Timestamp: 4ef4edc6
Exception Offset: 0000668a
Exception Code: c0000409
Exception Data: 00000000
OS Version: 6.0.6002.2.2.0.256.4
Locale ID: 1033
Additional Information 1: 6243
Additional Information 2: 0d5daf38e26c963685a835e6f40ff03d
Additional Information 3: aa53
Additional Information 4: 5d02a603659cce53ff840117c3a9c7a7
The BEX event name indicates a buffer overflow. But which buffer, I cannot tell.
Here's where it gets weird for me though...
When I change this method and add an unnecessary cout
line to it, it works with the Release profile:
int PathSubstitution::updateField(Field4memo &field, int record_id, int field_id) const
{
int type = field.type();
if((type == r4str) || (type == r4memo) || (type == r4unicode))
{
// THIS IS THE NEW LINE I ADDED RIGHT BELOW HERE!!!
cout << endl;
string value = field.str();
trim(value);
if(!substituteDriveLetters(value))
return -1;
if(!substituteGridMount(value))
return -1;
return field.assign(value.c_str(), value.length());
}
return r4success;
}
I can't tell why the method crashes with the Release profile or why adding the cout
line resolves the crashing issue. I'm uncomfortable just accepting the "cout
fixes it" answer -- can someone help me understand what my problem is here and why the cout
fixes it? How does the cout
call save me from a buffer overflow here?
Edit: some additional context for the call to this method was asked for. It's called in a loop. With the test input I'm using, it's called 4 times. The function that calls it looks like so:
int PathSubstitution::updateRecord(Data4 &dbf, int record_id) const
{
// Update all fields
int numFields = dbf.numFields();
for(int i = 1; i <= numFields; i++ )
{
Field4memo field(dbf, i);
int rc = updateField(field, record_id, i);
if(rc != r4success)
return rc;
}
return r4success;
}
Edit 2: Flushing the cout
buffer also fixes the overflow problem as long as cout.flush()
is called from within the PathSubstitution::updateField
method and before the return field.assign(value.c_str(), value.length());
line.
Edit 3: This is promising. If I comment out the calls the substituteDriveLetters()
and substituteGridMount()
methods the program doesn't crash. So it's something to do with those method calls (which use pcre
to do some regular expression string substitutions).
Edit 4: If I comment out just the substituteDriveLetters() method it works. So I've got a prime suspect now. This method is supposed to replace a drive letter in a path with the corresponding UNC value. None of the fields in my test input are file paths so this should be a null op as far as data transformation is concerned.
bool PathSubstitution::substituteDriveLetters(string &str, string::size_type offset) const
{
int offsets[6];
int groups = pcre_exec(drivePattern, NULL, str.c_str(), str.size(), 0, 0, offsets, sizeof(offsets));
if(groups < 0)
{
switch(groups)
{
case PCRE_ERROR_NOMATCH:
case PCRE_ERROR_PARTIAL:
return true;
case PCRE_ERROR_NOMEMORY:
cerr << "WARNING: Out of memory." << endl;
break;
case PCRE_ERROR_BADUTF8:
case PCRE_ERROR_BADUTF8_OFFSET:
cerr << "WARNING: Bad UNICODE string." << endl;
break;
default:
cerr << "WARNING: Unable to substitute drive letters (Err: " << groups << ")" << endl;
break;
}
return false;
}
char driveLetter = toupper(str[offsets[2]]);
DriveMap::const_iterator i = driveMap.find(driveLetter);
if(i == driveMap.end())
{
cerr << "ERROR: The " << driveLetter << " drive is not mapped to a network share." << endl;
return false;
}
string::iterator start = str.begin() + offsets[0];
string::iterator end = str.begin() + offsets[1];
str.replace(start, end, i->second);
return substituteDriveLetters(str, offsets[1]);
}