Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if jump buffer is valid or not (non-local jumps)

Tags:

c++

c

We have implemented "longjmp–Restore stack environment" in our code base. The longjmp routine is called by a particular error_exit function which can be invoked from anywhere.

Thus it is possible that when longjmp is called the setjmp routine may not have been called and the buffer can have invalid value leading to a crash.

Can I initialise the buffer to NULL or is there any check available to check for unset or invalid value. One way is that I can set a flag variable whenever setjmp is called, and I can check against that. But that is only a hack.

void error_exit()
{
    extern jmp_buf buf;
    longjmp(buf, 1);
    return 1;
}

Can I do something like this?

void error_exit()
{
    extern jmp_buf buf;

    if(buf)
       longjmp(buf, 1);

    return 1;
}

The code is mixed C/C++, I know I can replace setjmp and longjmp with C++ exception handling everywhere, but that is not possible now, can I instead catch longjmp with invalid buffer which leads to a crash?

like image 879
Kartik Anand Avatar asked Jan 29 '14 08:01

Kartik Anand


3 Answers

jmp_buf is not particularly well documented. In linux headers, you can find something like:

typedef int __jmp_buf[6];

struct __jmp_buf_tag {
  __jmp_buf __jmpbuf;       /* Calling environment.  */
  int __mask_was_saved;     /* Saved the signal mask?  */
  __sigset_t __saved_mask;  /* Saved signal mask.  */
};

typedef struct __jmp_buf_tag jmp_buf[1];

Setting it to zero and then test whole size may be a lost of time.

Personally, I would keep a pointer to this buffer, initializing it to NULL and setting it right before setjmp.

  jmp_buf physical_buf;
  jmp_buf *buf = NULL;
  ...
  buf = &physical_buf;
  if (setjmp(*buf)) {
    ...
  }

It is the same idea as having a separate flag. Moreover you can allocate jmp buffers dynamically if necessary.

like image 146
Marian Avatar answered Nov 03 '22 18:11

Marian


Knowing whether the buffer has been set or not is not a real problem, you could just have an auxiliary variable that holds that information.

The real problem is that you can't longjmp sideways, this is has undefined behavior and can't work for fundamental reasons. Once you returned from the function in which you have called setjmp the stack of that function is invalidated and overwritten by subsequent calls. So the setjmp context isn't valid anymore. And I am not talking of the jmp_buf itself, but of the stack state that is recorded in it.

So the only thing you can do is to have an auxiliary variable that records if you have set the buffer, and that you unset as soon that you leave the function with the setjmp in question.

like image 38
Jens Gustedt Avatar answered Nov 03 '22 18:11

Jens Gustedt


Good question.

As far as I can see from the POSIX standard (http://pubs.opengroup.org/onlinepubs/9699919799/functions/longjmp.html http://pubs.opengroup.org/onlinepubs/9699919799/functions/setjmp.html http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/setjmp.h.html) jmp_buf is opaque, save that the latter reference requires it to be an array type:

The <setjmp.h> header shall define the array types jmp_buf and ... sigjmp_buf.

I believe in practice you would be save bzero()-ing the buffer, and comparing it against zero before jumping to it, but in theory an all zero jmp_buf would be permissible on some as yet to be discovered POSIX system. If you were bothered about this (I wouldn't be), an option would be to set_jmp a dummy buffer to some jump point you never use, and then memcmp your jmp_buf against this dummy jmp_buf before doing the jump. That way you would only use legal (i.e. initialized by set_jmp) jmp_bufs.

An alternative would be to maintain a separate flag indicating whether it has been initialised (or wrap both in a struct).

I believe I should insert the mandatory warning on the manpage re set_jmp and long_jmp, in that it makes applications difficult to read, and has unpredictable effects on signal handling (the latter cured with sigsetjmp()). Also, in a C++ context it obviously does not unwind your exception handlers.

like image 1
abligh Avatar answered Nov 03 '22 18:11

abligh