C 即使存在非局部跳跃,也能可靠地检测递归

C 即使存在非局部跳跃,也能可靠地检测递归,c,recursion,setjmp,C,Recursion,Setjmp,我有一个特定的函数(信号处理器),我想检测递归,也就是说,找出函数是否直接或间接地调用了自己。棘手的一点是,函数在某一点上调用了一些不受其控制的代码,而这些代码可以做任何事情 通常,我只会写一些 void foo() { static int recursed = 0; if(recursed) { ... } recursed = 1; othercode(); recursed = 0; } 但在这种情况下,我担心otherc

我有一个特定的函数(信号处理器),我想检测递归,也就是说,找出函数是否直接或间接地调用了自己。棘手的一点是,函数在某一点上调用了一些不受其控制的代码,而这些代码可以做任何事情

通常,我只会写一些

void foo() {
    static int recursed = 0;
    if(recursed) {
        ...
    }
    recursed = 1;
    othercode();
    recursed = 0;
}
但在这种情况下,我担心
othercode
可能会使用
longjmp
或类似的方法来中断,从而导致
递归
保持在1。如果我的函数以这种方式跳出,我希望确保如果以后调用它,它不会认为自己是递归的(否则,它是
longjmp
'd的事实不是问题)


<强>注释:我认为LojMP 很有可能。

othercode
是一个链接的信号处理程序,来自于野生代码中的其他一些处理程序,并且确实存在使用
longjmp
恢复上下文(例如作为“故障保护”异常处理程序)的处理程序,例如
SIGSEGV
。请注意,在同步信号处理程序中使用
longjmp
通常是安全的。在任何情况下,我都不特别关心其他代码是否安全,因为它不在我的控制之下。

不确定代码到底是什么样子的,但是可以使用静态void*代替静态int。与其将其设置为1,不如将其设置为指向当前堆栈帧。除了检查它是否为非零之外,您还检查以确保
递归
之后的下一个堆栈帧的返回地址实际上指向foo代码中的某个位置,并且
递归
位于当前堆栈指针之上,即未弹出


听起来非常脆弱,依赖于体系结构。

根据POSIX标准,longjmp()不是从信号处理程序内部安全调用的调用之一。在考虑这个问题之前,文档中说,您需要确保正在调用的代码使用sigsetjmp()和siglongjmp()


如果您正在调用的代码跳出信号处理程序,那么我看不出您如何知道何时更新
递归
变量,除非您还控制一个回调函数,该未知代码将回调函数回调回您的应用程序

你担心longjmp有可能,或者只是担心它有可能?@Patashu:有可能。请参阅我的编辑。问题标题应该编辑得更具体一些,比如“即使使用了longjmp也能检测到重新进入”。@Patashu:谢谢你的建议,编辑如果你知道jongjmp的去向,您可以将代码放在那里以清除递归指示。或者,在对foo发出调用指令之后,从foo的堆栈帧点查看返回地址可能更简单,但这肯定是不可移植的。我考虑过这一点。它工作得非常好,除了一个很小的例子:假设
othercode
跳出到
foo
上方的一个函数,该函数调用另一个分配(但不完全使用)巨大堆栈缓冲区的函数,然后触发
foo
。如果我不走运,巨型堆栈缓冲区会将堆栈指针移动到
foo
下面,并保留原始
foo
堆栈,使其看起来非常像递归。当然,这是不太可能的,这就是我可能最终使用此解决方案的原因。但是,理想情况下,我希望找到一个没有这个问题的解决方案。也许我考虑的是基/帧指针,而不是堆栈指针。没关系,同样的问题。已分配但未初始化的缓冲区可能位于前一帧中。这些仅适用于异步信号。我正在为
SIGSEGV
和其他正常同步信号编写一个信号处理程序,因此这不应该是一个问题。