Does this code contain a hidden error?

The following code:

  • Works well when compiling with gcc version 4.4.5 (Ubuntu / Linaro 4.4.4-14ubuntu5 / 32bits)
  • Works well when compiling with MSVC10 (Win7 / 32bits)
  • Failure to work with gcc version 4.5.2 (MinGW on Win7 / 32bits)

main.cpp :

# include <iostream> # include <csetjmp> # include <stdexcept> using namespace std ; void do_work(jmp_buf context) { try { throw runtime_error("Ouch !") ; } catch(exception & e) { } longjmp(context, -1) ; //BP1 } int main(int, char *[]) { jmp_buf context ; try { if( setjmp(context) != 0 ) { throw runtime_error("Oops !") ; //BP2 } do_work(context) ; } catch(exception & e) { cout << "Caught an exception saying : " << e.what() << endl ; } } 

I tried debugging it, but the program is behaving strangely. Sometimes I could go past the first breakpoint (BP1), then crash at BP2, and sometimes the control never reaches BP1, for example, if the program gets stuck in an infinite loop. I can not say more with my debugging skills.

This code is the smallest I could get by exhibiting strange behavior with MinGW 4.5. I also noticed that:

  • If I replace the call to the do_work function do_work its contents, the program will work fine.
  • If I delete the try{ ... } catch(...){ } do_work inside do_work , the program works fine.
  • Optimization flags do not affect (always crash).

I am aware of the problems of setjmp/longjmp in C ++ code, but I have to use it to interact with some legacy C code.

My question is:

  • Is this an erroneous / erroneous / erroneous code? Or does MinGW 4.5 process code incorrectly? (Itโ€™s tough and presumptuous to blame the tool, but I suspect that it has some settings).

Thanks for any advice.

Please try if necessary.

+6
source share
3 answers

The Unix longjmp (3) man file says:

Longjmp () routines may not be called after the routine that setjmp () routines call return

I think this explains your concern that "sometimes control never reaches BP1." I donโ€™t think that โ€œworks fineโ€ is a reliable solution. I would prefer that it randomly work fine and, as a rule, ruin the stack.

There are some clear recommendations to consider when working with longjmp / setjmp substitution with C ++ exceptions to avoid crashes and undefined behavior:

  • Do not use setjmp / longjmp in C ++ programs.
  • If you use the setjmp / longjmp functions in a program where exceptions may occur, you are safe if they do not interact.
  • Never execute longjmp in or from a try and catch clause.
  • Never longjmp over the initialization point of automatic objects.
  • Never break the pasy point of destruction of automatic objects, especially if the destructor is nontrivial.
  • Never throw out a signal handler.
  • Never call longjmp from a nested signal handler.
  • The behavior of longjmp from location X to location Y remains predictable and valid as long as the exception thrown at X and caught in X has the same effect.
  • If you mix setjmp / longjmp with exceptions, don't expect portability.
  • Instead, refer to the relevant information in the documentation compiler that you are using. For example, if you are using Visual C ++, read Use setjmp / longjmp

This question mentions the inherited C code in programs written in C ++. In a review of one of the Boost libraries, there was an interesting discussion of sjlj problems in the jpeg library. The discussion was long, but here is the essence with the recommended parameters .

+3
source

C ++ only makes one additional restriction on using longjmp() :

If any automatic objects are destroyed by controlling the transfer of the excluded exception to another (target) point in the program, then calling longjmp (jbuf, val) at the throw point that transfers control to the same (target) point has undefined behavior. (18.7)

You seem to know about this, and your program does not. I vote for compiler errors.

+1
source

longjmp and setjmp are c-functions, calling them using the C ++ exception handling semantics and object destruction semantics - this is undefined behavior, which means it executes before execution, whether it works or not (your testing shows this, SEH stack unind semantics breaks things, depending on the type used, iirc your workers use dwarf2)

0
source

Source: https://habr.com/ru/post/900340/


All Articles