Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does this code contain a hidden bug?

Tags:

c++

mingw

setjmp

The following code :

  • Runs fine when compiled with gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5/32bits)
  • Runs fine when compiled with MSVC10 (Win7/32bits)
  • Crashes when running 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 behaves strangely. Sometimes I could get past the first breakpoint (BP1), then crash at BP2, and sometimes control never reachs BP1, like if the program is stuck in an infinite loop. I cannot say more with my debugging skills.

This code is the minimal I could get that exhibits the strange behavior with MinGW 4.5. I also noticed that :

  • If I replace the do_work function call by its content, the program runs fine.
  • If I remove the try{ ... } catch(...){ } block inside do_work, the program runs fine.
  • Optimization flags have no effect (always crashing).

I'm aware of the setjmp/longjmp issues in C++ code, but I'm forced to use it to interface with some legacy C code.

My question :

  • Is this a faulty/buggy/erroneous code ? Or is MinGW 4.5 mishandling the code ? (It is harsh and presumptuous to blame the tool, but I suspect some settings in it).

Thanks for any advice.

Please retag if necessary.

like image 763
overcoder Avatar asked Oct 29 '11 12:10

overcoder


1 Answers

The longjmp(3) man page on Unix says:

The longjmp() routines may not be called after the routine which called the setjmp() routines returns

I think it explains explains your concern that "sometimes control never reachs BP1". I don't think "runs fine" is reliable judgement. I'd rather expect it randomly runs fine and generally is messing up with stack.

There are a few clear recommendations that should be taken into account while working mixing longjmp/setjmp with C++ exceptions in order to avoid crashes and undefined behaviour:

  • Do not use setjmp/longjmp in C++ programs.
  • If you use setjmp/longjmp functions in program where exceptions can occur, you are safe as long as they do not interact.
  • Never longjmp in or out of try clause and catch clause.
  • Never longjmp over point of initialization of automatic objects.
  • Never longjmp pasy point of destruction automatic objects, especially if destructor is non-trivial.
  • Never throw from signal handler.
  • Never invoke longjmp from nested signal handler.
  • Behaviour of longjmp from location X to location Y stays predictable and valid as long as if exception thrown at X and caught at X would have the same effect.
  • If you mix setjmp/longjmp with exceptions, do not expect portability.
  • Instead, refer to relevant details in documentation compiler you are using. For example, if you use Visual C++, read Use setjmp/longjmp

The question mentions dealing with legacy C code in programs written in C++. There has been an interesting discussion on sjlj issues in jpeg library during review of one of Boost libraries. The discussion was long but here is essence with recommended options.

like image 176
mloskot Avatar answered Sep 18 '22 15:09

mloskot