简体   繁体   English

检查跳转缓冲区是否有效(非本地跳转)

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

We have implemented "longjmp–Restore stack environment" in our code base. 我们在代码库中实现了“longjmp-Restore堆栈环境”。 The longjmp routine is called by a particular error_exit function which can be invoked from anywhere. longjmp例程由特定的error_exit函数调用,该函数可以从任何地方调用。

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. 因此,当调用longjmp时,可能没有调用setjmp例程,并且缓冲区可能具有导致崩溃的无效值。

Can I initialise the buffer to NULL or is there any check available to check for unset or invalid value. 我可以将缓冲区初始化为NULL还是可以检查是否有未设置或无效的值。 One way is that I can set a flag variable whenever setjmp is called, and I can check against that. 一种方法是每当调用setjmp时我都可以设置一个标志变量,我可以检查它。 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? 代码是混合的C / C ++,我知道我可以在任何地方用C ++异常处理替换setjmplongjmp ,但现在这是不可能的,我可以用无效缓冲区捕获longjmp导致崩溃吗?

jmp_buf is not particularly well documented. jmp_buf没有特别好记录。 In linux headers, you can find something like: 在linux标题中,你可以找到类似的东西:

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. 就个人而言,我会保留一个指向此缓冲区的指针,将其初始化为NULL并在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. 此外,您可以根据需要动态分配jmp缓冲区。

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. 真正的问题是你不能longjmp横行,这是有不确定的行为,并且由于根本原因无法工作。 Once you returned from the function in which you have called setjmp the stack of that function is invalidated and overwritten by subsequent calls. 从您调用setjmp的函数返回后,该函数的堆栈将被无效并被后续调用覆盖。 So the setjmp context isn't valid anymore. 所以setjmp上下文不再有效。 And I am not talking of the jmp_buf itself, but of the stack state that is recorded in it. 我不是在谈论jmp_buf本身,而是谈论它中记录的堆栈状态。

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. 所以你唯一能做的就是有一个辅助变量来记录你是否已经设置了缓冲区,并且一旦你将函数与setjmp放在一起就会解除设置。

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: 据我所知,从POSIX标准( 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是不透明的,除了后一个引用要求它是一个数组类型:

The <setjmp.h> header shall define the array types jmp_buf and ... sigjmp_buf . <setjmp.h>头文件应定义数组类型jmp_buf和... 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. 我相信在实践中你会保存bzero()缓冲区,并在跳转到它之前将它与零进行比较,但理论上,在一些尚待发现的POSIX系统中,所有零jmp_buf都是允许的。 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. 如果你(我不会)打扰这一点,一个选择是set_jmp虚拟缓冲区你永远不会使用一些跳跃点,然后memcmpjmp_buf反对这种假jmp_buf做前跳。 That way you would only use legal (ie initialized by set_jmp ) jmp_buf s. 这样你只能使用合法的(即由set_jmp初始化) jmp_buf

An alternative would be to maintain a separate flag indicating whether it has been initialised (or wrap both in a struct ). 另一种方法是维护一个单独的标志,指示它是否已经初始化(或者在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() ). 我相信我应该在manpage re set_jmplong_jmp上插入强制警告,因为它使应用程序难以阅读,并且对信号处理有不可预测的影响(后者用sigsetjmp() )。 Also, in a C++ context it obviously does not unwind your exception handlers. 此外,在C ++上下文中,它显然不会展开您的异常处理程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM