Search code examples
clanguage-lawyerc89setjmpvariadic-functions

Is it okay to longjmp before calling va_end?


In this Q&A it is established that you should always call va_end():

What exactly is va_end for? Is it always necessary to call it?

But what if a piece of code longjmp's before you reach the va_end? Is there any promise on va_end's part that it will be okay? Or conceptually might it (for example) do a memory allocation in va_start() that would be leaked, vs. just using stack tricks?


Solution

  • The C99 rationale explicitly states that va_start may allocate memory that ends up freed by va_end, exactly what you guessed in your question:

    7.15.1.2 The va_copy macro

    [...]

    30 A much simpler approach is to copy the va_list object used to represent processing of the arguments. However, there is no safe way to do this in C89 because the object may include pointers to memory allocated by the va_start macro and destroyed by the va_end macro.
    The new va_copy macro provides this safe mechanism.

    [...]

    So yes, you need to invoke va_end before a longjmp. At the very least you'd otherwise have a memory leak on such an implementation.


    Supposedly Pyramid OSx had an implementation where memory allocations were performed by va_start. Function arguments were passed in registers. This was the case even for variadic functions. It may have pre-dated ANSI C's invention of function prototypes, meaning the caller wouldn't know whether it was dealing with a variadic function. va_start allocated memory, presumably to store the function parameter values in a way that va_arg could easily access it. va_end freed the allocated memory.

    Its implementation of va_start and va_end actually required matching va_start and va_end syntactically, because it was one that used unbalanced braces, so ANSI C already disallowed that implementation, but the same principle could be made to work while having matching braces.

    I can find very little concrete information on this implementation, it's just bits and pieces on Usenet in the late '80s, early '90s. What little I did find may be incomplete or even just plain wrong. More details are very welcome, especially by anyone who used this implementation themselves.