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

时间:2014-01-29 08:52:41

标签: c++ c

我们在代码库中实现了“longjmp-Restore堆栈环境”。 longjmp例程由特定的error_exit函数调用,可以从任何地方调用。

因此有可能在调用longjmp时,可能没有调用setjmp例程,并且缓冲区可能具有导致崩溃的无效值。

我可以将缓冲区初始化为NULL,还是可以检查是否有未设置或无效的值。一种方法是每当调用setjmp时我都可以设置一个标志变量,我可以检查它。但这只是一个黑客攻击。

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

我可以这样做吗?

void error_exit()
{
    extern jmp_buf buf;

    if(buf)
       longjmp(buf, 1);

    return 1;
}

代码是混合的C / C ++,我知道我可以在任何地方用C ++异常处理替换setjmplongjmp,但现在这是不可能的,我可以用longjmp来代替{{1}}导致崩溃的无效缓冲区?

3 个答案:

答案 0 :(得分:2)

jmp_buf没有特别好的记录。在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];

将其设置为零然后测试整个尺寸可能会浪费时间。

就个人而言,我会保留一个指向此缓冲区的指针,将其初始化为NULL并在setjmp之前设置它。

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

与拥有单独的旗帜的想法相同。此外,您可以根据需要动态分配jmp缓冲区。

答案 1 :(得分:2)

知道缓冲区是否已设置不是一个真正的问题,你可以只有一个包含该信息的辅助变量。

真正的问题是,你不能longjmp横向,这是未定义的行为,并且由于根本原因无法工作。从您调用setjmp的函数返回后,该函数的堆栈将被无效并被后续调用覆盖。因此setjmp上下文不再有效。我不是在谈论jmp_buf本身,而是谈论记录在其中的堆栈状态。

所以你唯一能做的就是有一个辅助变量来记录你是否设置了缓冲区,并且一旦你将问题留给了setjmp,你就会立即取消设置。

答案 2 :(得分:1)

好问题。

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

  

<setjmp.h>标头应定义数组类型jmp_buf和... sigjmp_buf

我相信在实践中你会保存bzero() - 缓冲区,并在跳转之前将它与零进行比较,但理论上,对于某些人而言,所有零jmp_buf都是允许的。被发现POSIX系统。如果你对此感到困扰(我不会),可以选择set_jmp虚拟缓冲区到你从未使用的某个跳转点,然后memcmp你的jmp_buf对此在做跳跃之前做假jmp_buf。这样你就只能使用合法的(即由set_jmpjmp_buf s。

初始化

另一种方法是维护一个单独的标志,指示它是否已被初始化(或在struct中包装)。

我认为我应该在联机帮助页set_jmplong_jmp上插入强制警告,因为它会使应用程序难以阅读,并且对信号处理产生不可预测的影响(后者使用{{ 1}})。此外,在C ++上下文中,它显然不会展开您的异常处理程序。