Link to home
Start Free TrialLog in
Avatar of Dave_Shields
Dave_Shields

asked on

Reinterpret cast generates fatal stack overflow error?

I am using MFC and managed code in the same file.
When using a reinterpret_cast to cast an internet connection pointer, I get a fatal stack overflow error.  The problem is intermittent in that it might fail twice, and then on the next 2 tries it will work.  The code is:
CInternetSession mysession;
CInternetFile* pFile = NULL;
try
   {
   // We know for sure that this is an Internet File so the cast is safe
   cout << "Inside TRY BLOCK" << endl;
   pFile = NULL;
   pFile = reinterpret_cast<CInternetFile*>(mysession.OpenURL(pszURL));
   cout << "pFile in TRY is : " << pFile << endl;
   }

The output statements are for runtime diagnostics.
Avatar of jkr
jkr
Flag of Germany image

The cast itself for sure will not cause any ovreflow problems. Actually, it is not even necessary. Just make that

   pFile = mysession.OpenURL(pszURL);

Are you sure the error code is not misinterpreted?
Avatar of AlexNek
AlexNek

I'm personally would never casting up for foreign code in addition
"You never create a CInternetFile object directly."
http://msdn2.microsoft.com/en-us/library/3c69cwt5(VS.80).aspx
What is your stack look like when error come? I agree with Jkr - problem is somewhere else.
Looks like some exceptions are happening in this case? How are you calling the try-catch, are you calling goto? This problem is not with reinterpret_cast as I also agree with  the previous posts.Some code is getting called in a loop.Use CInternetException class. Put try- catch block for all the functions.
Best Regards,
DeepuAbrahamK
Avatar of Dave_Shields

ASKER

JKR
When the code is compiled without the cast it doesn't compile, but gives the error

error C2275: 'CInternetFile' : illegal use of this type as an expression                                    
DeepuAbrahamK:
One cannot catch a stack overflow exception with try-catch, as the catch itself would have to use the stack.
AlexNek:
How can one see the stack after a stack overflow error?  Happy to try anything.
ALL
This is an intermittent problem.  How this is possible with a computer program I don't know.
Here are my results.  F = fail; S = succeed
reinterpret_cast
FFSSSFFFSFFFFFSFFFFSSF
old cast
FFFFFFSSSSFFSFSFSFFFSF
dynamic_cast
SSFFFSFFFSFFFFFFFSSFF
Hm, 'OpenURL()' returns a 'CStdioFile*', which is a base class for a 'CInternetFile', thus the conversion should work implicitly. Does

pFile = (CInternetFile*)mysession.OpenURL(pszURL);

work? Also, ist the URL argument valid when that happens? You could test that using

ASSERT(IsBadStringPointer(pszURL,REASONABLE_MAX_VALUE));
You search in the wrong place, I think. Randomly fault can be produced by randomly data, like not initialized variable.
>How can one see the stack after a stack overflow error
Under debug mode in the Call  Stack Window. You can see a long, long list of calls. I saw it already.
jkr:
pFile = (CInternetFile*)mysession.OpenURL(pszURL);
gives
FFFFFFSSSSFFSFSFSFFFSF
What about verifying the URL parameter?
Try to ceate a logging class something like it.

CMyLog {
  public:
      CMyLog(cons char* pText) { m_Text = pText; //write m_pText in file - Started}
      ~CMyLog() {//write m_Text in file, Finished}
  private:
   CString m_Text'
};

{
     CMyLog("Debug point 1");
......

}
Stack would get corrupted if you have any goto statement in catch (If it is not handled properly).If the connection is failing why don't you write the code in such a way that it try a few attempts till it connects.I remember I had done some time back while writing an ftp client in windows which connects to mainframe system.Writing logs/audit trail is also not a bad idea.
Best Regards,
DeepuAbrahamK
jkr
The URL parameter is the same as an MFC program which connects and reads text.
Dave
AlexNek:
I would need more help to  set up and use a logging class.
Dave
DeepuAbrahamK:
The catch code follows:
catch (CInternetException* pEx)
{
   // If anything went wrong catch it here
   pscUnconnectable->Add(path);
   cout << "Problem setting up pFile"  << endl;
   cout << pEx << endl;
   pFile = NULL;
}      
ALL
The code is from Kate Gregory's book "Special edition using Visual C++.Net, Capter 10, page 310 - MFC applicaion called Query.  It works well.  As a matter of fact if it receieves a 404 error, it will read and display the error page.
The only difference is that Query is a totally MFC project, and my code is in a combination of MFC and managed code.
The main mystery is that my project will work randomly, and fail equally randomly in successive trials.  I could understand better if it failed every time.   But repeated trials with no changes results in random success and failure.
Perplexed, Dave
>I would need more help to  set up and use a logging class.
It is no problem but is very easy class. You can realize it with cout too and you need only one line instead two.
cout << "Inside TRY BLOCK" << endl;
....
 cout << "pFile in TRY is : "  << endl;

In my opinion it is important to know from which place apllication is crashed.


>>>> returns a 'CStdioFile*', which is a base class for a
>>>> 'CInternetFile', thus the conversion should work implicitly.
No, if  'CInternetFile' is derived from ''CStdioFile' you can't assign a CStdioFile* to a  CInternet* without a cast. In case the memory wasn't already corrupted, a dynamic_cast is required between pointers of derived classes. A C cast also should do it. A reinterpret cast would ignore virtual pointer tables what isn't appropriate here (but nevertheless should give no problems here as no multiple inheritance is involved).

You easily should see that the cast is not the problem by replacing

    CInternetFile* pFile = NULL;

by

     CStdioFile* pFile = NULL;

You should get the same kind of failures/success as with using the cast. I would think that it is a problem of mixing managed and unmanaged code but without further information it is only a shot from the hip.

Regards, Alex
 
itsmeandnobodyelse:
When I substitute CStdioFile* for CInternetFile* I get
error C2664: 'ScriptRemover' : cannot convert parameter 1 from 'CStdioFile *' to 'CInternetFile *'
failure rate is about the same for a C style cast.  A function style cast will not compile.
If the garbage collector were to move an MFC pointer at the wrong time, intermittent failure would be the result.  Is it possible to __pin an MFC pointer such as CInternetFile*??

I can't imagine such an error in this construction
CStdioFile* pFile = NULL;
   pFile = mysession.OpenURL(pszURL);

Do you run your code in additional thread?
Where is exatly definition of mysession: in the function or as data member in your application class?
AlexNek
I am not using threads in the program.  It is not a very long program, so it is written as one long program with few functions.  The code that I've quoted is in the "Main" part of the program.
I have a screenshot of the debug output.  It is at
http://aquilifer-web-design.com/query_error_mar-19-2007.htm
>>>> error C2664: 'ScriptRemover' : cannot convert parameter 1 from
>>>> 'CStdioFile *' to 'CInternetFile *'
Without casting you can't call member functions of CinternetFile that were not virtual. The purpose of the substuitution was not to provide a fully working alternative but to show that the problem exists without cast as well.
Do you know that under Debug/Exceptions you can set options "Break when an exption is:" Trown?
The first exception you got is an access violation (0x00000005). That is a typical for illegal pointer access, e. g. using a NULL pointer or work with a pointer that already was deleted. The latter would explain why it works sometimes. If the memory the (invalid) pointer was pointing to was not reused it works. Otherwise it fails. Try that what AlexNek had suggested. Go to the Debug\Exceptions page and select "Access Violation" from the list of general exceptions. Then, you can set that the debugger will stop immediately when the exception occurs. Run your prog and when it breaks check the call stack to find out what statement and what pointer have caused the (first) access violation. Also check the this pointer which might be invalid as well.

Regards, Alex
 
itsmeandnobodyelse:
I set the debug/exceptions as you suggested.
I the ran the program from Debug > Start 15 times without failure.
I the ran the program from Debug > Start without debugging.  It failed the first time.  The debug output is at http://www.aquilifer-web-design.com/query_debug_output_mar20-2007.htm
I don't know enough to read it.
Yes, that is the problem with invalid pointers. If you change something - here to force the debugger to watch on the first-chance exceptions - it changes its memory management ... and the error wasn't reproducable because of that ...

The debug output simply means that there is no error (beside of not loading some debugger symbols for system libraries).

Ok, I hoped to get the statement where the wrong pointer was used but if the error doesn't occur, we need to make a different approach. Add after any statement which might crash because of a pointer or call the following statement:

    <original statement>
    OutputDebugString("<original statement> \n";

e. g.  

     pFile = reinterpret_cast<CInternetFile*>(mysession.OpenURL(pszURL));
     OutputDebugString("pFile = reinterpret_cast<CInternetFile*>(mysession.OpenURL(pszURL));\n");

Instead of outputting the statement you also can output a number which was incremented for the next statement:

     pFile = reinterpret_cast<CInternetFile*>(mysession.OpenURL(pszURL));
     OutputDebugString("20\n");
     cout << "pFile in TRY is : " << pFile << endl;
     OutputDebugString("21\n");

These output statements hwich show in the output window of the IDE should tell how far it comes before crash. Try to reproduce the crash. Check where the output stops and evaluate the next statement where the output wasn't shown. Set a breakpoint there and examine all variables. If it is a call step into until crash. If the function you stepped in is your function you may use more OutputDebugString calls to spot the statement which crashes.

What you can do additionally is to check *all* member variables were initialized in the constructor. Initialize *all* local variables espcially pointers. Never use a pointer in two containers. If using a pointer to a local variable never return that pointer cause the object was deleted after leaving the function and the pointer becomes invalid.


Regards, Alex
 

itsmeandnobodyelse:
I found the pFile had not been closed or deleted, so I added that code to stop the memory leak.
Ran the program from Debug > Start 14 times without failure.
From Debug > Start without debugging it failed on the fourth attempt.
The stack output is at:
http://www.aquilifer-web-design.com/query_error_stack_mar20-2007.htm
The stack overflow is due to a NULL pointer (function), which you can see in the call stack.

In one of your previosu posts we saw two first-chance exceptions ("access violation"). First chance exceptions were exceptions caught by a try catch block. So, they normally can ignored cause the developer added code to handle them. However access violation means use of invalid or NULL pointer what hardly can be handled properly even if it is in a try catch handler. Still you need to find out what pointer goes invalid.

>>>> so I added that code to stop the memory leak.
Your main problem is not memory leaks but the contrary - a pointer which has no valid allocation, either cause it was freed or either it was not properly initialized.

>>>> without debugging it failed on the fourth attempt.
That looked like timing problems. You don't have a crash when you slow down things by the debugger. You said you have no threads? But it really looked like a multi-threading issue. Maybe when calling unmanaged code from managed code. Can you tell how you do that? Is the code above running in a dll? How does it get it's inputs and how does it return the results?

Regards, Alex
itsmeandnobodyelse:
At the moment I cannot step through the code, as the debugger just runs through it all, right through breakpoints.  Besides it doesn't fail in debugger mode.
It only fails when starting without debugging.
When it fails, the output lines indicate that the trouble lies with
pFile = reinterpret_cast<CInternetFile*>(mysession.OpenURL(pszURL));
Did you try to use Bounds Checker or somthing similar?
itsmeandnobodyelse:
The interlink between managed and unmanaged code is solely through CString and String*.  CString to interface with MFC, and String* to handle the strings when files are read.
//  CString  to String*
String* cstrtostrp(CString cstr)
{
      String* str(cstr);
      return str;
}
//  String* to CString
CString strptocstr(String *str)
{
      CString Cstr3(str);
      return Cstr3;
}
I can't see any difficulty on this front, as everything looks fine when printed out.

One interesting point - the Compiler will not let me close session with session.Close(), as it should.
Any idea why??
AlexNek:
Did you try to use Bounds Checker or somthing similar?

I'm not familiar with Bounds Checker.  will research a little.

itsmeandnobodyelse:
By trial and error I have found that when the following code is commented out, the program works 20 times without fail.  when the code is included, it will only run 7 or 8 times before failing.
for (i=0; i < n; i++)
      {
            dprevious[i] = 0.0;
            assert (i < n);
            Console::WriteLine(S"Previous # {0}\n", __box(i) );
      }
Any idea why??
ASKER CERTIFIED SOLUTION
Avatar of itsmeandnobodyelse
itsmeandnobodyelse
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>>> up to it "works" 100% safe
How will you make that sure? Even if it crashes once a month it most likely is not acceptable. You better would try to reliable make it crash rather than commenting statements which look suspicious. For example you can clear all member data (set to 0 or space)  prior to delete a class instance. Set the value 0xdeaddead to all pointers that were deleted. If you see that address later in the debugger you know it was a deleted pointer which made the trouble. You also could set the debugger to stop immediately at access violation *and* make automatic tests - say 1000 or 10000 calls - where maybe one will crash and show you the pointer that was wrong.

Regards, Alex
 
>>> The interlink between managed and unmanaged code is solely through CString
I don't know much of managed code but the first thing when mixing managed and unmanaged is that the unmanaged runs in a separate dll. Can you post the exported interface of that dll? You know that allocating memory in your prog and deleting that memory in the dll most likely doesn't work? So, all interfaces should provide a fxed sized buffer (string, array, variables)  where the receiving dll makes no changes of the size. E. g. a CString passed by pointer or by reference to the dll may *not* be expanded within the dll cause that definitively makes problems when freeing the memory. Moreover, it also makes problems in the dll cause CString works with static members which exist both in the dll and in the managed prog (or marshaler). So CStrings should be passed by value (the dll makes a copy then) or by const pointer or const reference only. If you want to receive data via a CString, you need to allocate a big enough buffer at the sender side, e. g. by CString::GetBuffer(). The dll only may use the passed buffer and never try to increase it, e. g. by operator+=.

Regards, Alex
>How will you make that sure? Even if it crashes once a month it most likely is not acceptable.
I would be agree with you if we are talking in general. In general I'll never be 100% sure that our program has no more errors. But now we want to catch only one error. We know that this error must to come in X tries if it not come we can decide that this errornot presewntin code. Ok, ok our decision can we wrong but possibility to isolate the wrong part of code is very high, I think. Of course, I can suggest this method only if any others give us nothing.
No comment has been added to this question in more than 21 days, so it is now classified as abandoned.

I will leave the following recommendation for this question in the Cleanup Zone:
  Split: itsmeandnobodyelse {http:#18762263} & AlexNek {http:#18762448}

Any objections should be posted here in the next 4 days. After that time, the question will be closed.

Sean
EE Cleanup Volunteer