程序通过纯粹的意志力溢出调用堆栈

时间:2022-09-06 14:18:02

Here's the call stack from a user's crash report:

这是来自用户崩溃报告的调用堆栈:

Thread 0 Crashed:  Dispatch queue: com.apple.main-thread
0   com.growl.GrowlSafari           0x179d383c writeWithFormat + 25
1   com.growl.GrowlSafari           0x179d388e writeWithFormat + 107
2   com.growl.GrowlSafari           0x179d388e writeWithFormat + 107
3   com.growl.GrowlSafari           0x179d388e writeWithFormat + 107
4   com.growl.GrowlSafari           0x179d388e writeWithFormat + 107
5   com.growl.GrowlSafari           0x179d388e writeWithFormat + 107

The trace cuts off at frame 511.

迹线在框511处切断。

Here's writeWithFormat:

int writeWithFormat(FILE *file, NSString *format, ...) {
    va_list args;
    va_start(args, format);
    int written = writeWithFormatAndArgs(file, format, args);
    va_end(args);
    return written;
}

As you can see, it doesn't call itself.

正如你所看到的,它并没有自称。

Here's the function it does call:

这是它调用的功能:

int writeWithFormatAndArgs(FILE *file, NSString *format, va_list args) {
    return 0;
    return fprintf(file, "%s\n", [[[[NSString alloc] initWithFormat:format arguments:args] autorelease] UTF8String]);
}

(As you can guess, this is logging code that's inactivated.)

(你可以猜到,这是记录失效的代码。)

So, how does this code result in that stack trace?

那么,这段代码如何导致堆栈跟踪?


Disassembly using otx:

使用otx进行反汇编:

_writeWithFormatAndArgs:
    +0  00000f68  55                    pushl       %ebp
    +1  00000f69  89e5                  movl        %esp,%ebp
    +3  00000f6b  31c0                  xorl        %eax,%eax
    +5  00000f6d  c9                    leave
    +6  00000f6e  c3                    ret

_writeWithFormat:
    +0  00001823  55                    pushl       %ebp
    +1  00001824  89e5                  movl        %esp,%ebp
    +3  00001826  83ec10                subl        $0x10,%esp
    +6  00001829  31c0                  xorl        %eax,%eax
    +8  0000182b  c9                    leave
    +9  0000182c  c3                    ret

2 个解决方案

#1


3  

In the full crash report, you can see in the binaries section that the user has two copies of GrowlSafari loaded:

在完整崩溃报告中,您可以在二进制文件部分中看到用户已加载两个GrowlSafari副本:

 0x140c000 -  0x140efff +com.growl.GrowlSafari 1.1.6 (1.1.6) <1E774BDF-5CC5-4876-7C66-380EBFEAF190> /Library/InputManagers/GrowlSafari/GrowlSafariLoader.bundle/Contents/PlugIns/GrowlSafari.bundle/Contents/MacOS/GrowlSafari
0x179d2000 - 0x179d4ff7 +com.growl.GrowlSafari 1.2.1 (1.2.1) <10F1EF69-D655-CCEE-DF3A-1F6C0CF541D3> /Applications/GrowlSafari.app/Contents/Resources/GrowlSafari.bundle/Contents/MacOS/GrowlSafari

The code I showed in the question is from 1.2.1 (but there's a good chance it hasn't changed since 1.1.6) and the disassembly is of 1.2.1.

我在问题中展示的代码来自1.2.1(但很可能它自1.1.6以来没有改变)并且反汇编是1.2.1。

This probably is the cause of the problem, especially as the recursion appears to actually be in a swizzled method (thanks to @_karsten_ on Twitter for pointing this out).

这可能是问题的原因,特别是因为递归看起来实际上是一个混合的方法(感谢Twitter上的@_karsten_指出这一点)。

#2


1  

A tail call somewhere in there could be causing one or more functions to disappear from the stack trace which, of course, makes debugging a load of fun.

在那里的某个地方调用尾部可能会导致一个或多个函数从堆栈跟踪中消失,这当然会使调试充满乐趣。

Off the top of my head, I could think of two possible scenarios that could cause this:

在我的脑海中,我可以想到两种可能导致这种情况的可能情况:

  • the format string is a subclass of NSString or there is a category somewhere that is causing a call through to writeWithFormat() during that writeWithFormat(). Custom logging code will do that sometimes -- it is quite frighteningly easy to write some generic custom logging code that'll happily call back into itself. Been there, done that. Many times, sadly.

    格式字符串是NSString的子类,或者某个类别在某个地方导致在writeWithFormat()期间调用writeWithFormat()。自定义日志记录代码有时会这样做 - 编写一些通用的自定义日志记录代码非常容易,这些代码可以很好地回调自身。去过也做过。很多次,可悲的是。

  • a perversity in memory is causing the recursion; corrupted object or something.

    内存中的异常导致递归;腐败的物体或其他东西。

Both a bit of a grasp at straws. Post the entire crash report.

对稻草都有点把握。发布整个崩溃报告。


Two versions of the bundle swizzling the runtime... all bets are off until the crash is reproduced in the face of only one of those bundles. I'd bet that is the problem.

捆绑的两个版本在运行时间内进行了调整...所有的赌注都关闭,直到崩溃再次出现在其中一个捆绑包中。我敢打赌这就是问题所在。

I also bet that there is some related console spew.

我还打赌,有一些相关的控制台喷出。

#1


3  

In the full crash report, you can see in the binaries section that the user has two copies of GrowlSafari loaded:

在完整崩溃报告中,您可以在二进制文件部分中看到用户已加载两个GrowlSafari副本:

 0x140c000 -  0x140efff +com.growl.GrowlSafari 1.1.6 (1.1.6) <1E774BDF-5CC5-4876-7C66-380EBFEAF190> /Library/InputManagers/GrowlSafari/GrowlSafariLoader.bundle/Contents/PlugIns/GrowlSafari.bundle/Contents/MacOS/GrowlSafari
0x179d2000 - 0x179d4ff7 +com.growl.GrowlSafari 1.2.1 (1.2.1) <10F1EF69-D655-CCEE-DF3A-1F6C0CF541D3> /Applications/GrowlSafari.app/Contents/Resources/GrowlSafari.bundle/Contents/MacOS/GrowlSafari

The code I showed in the question is from 1.2.1 (but there's a good chance it hasn't changed since 1.1.6) and the disassembly is of 1.2.1.

我在问题中展示的代码来自1.2.1(但很可能它自1.1.6以来没有改变)并且反汇编是1.2.1。

This probably is the cause of the problem, especially as the recursion appears to actually be in a swizzled method (thanks to @_karsten_ on Twitter for pointing this out).

这可能是问题的原因,特别是因为递归看起来实际上是一个混合的方法(感谢Twitter上的@_karsten_指出这一点)。

#2


1  

A tail call somewhere in there could be causing one or more functions to disappear from the stack trace which, of course, makes debugging a load of fun.

在那里的某个地方调用尾部可能会导致一个或多个函数从堆栈跟踪中消失,这当然会使调试充满乐趣。

Off the top of my head, I could think of two possible scenarios that could cause this:

在我的脑海中,我可以想到两种可能导致这种情况的可能情况:

  • the format string is a subclass of NSString or there is a category somewhere that is causing a call through to writeWithFormat() during that writeWithFormat(). Custom logging code will do that sometimes -- it is quite frighteningly easy to write some generic custom logging code that'll happily call back into itself. Been there, done that. Many times, sadly.

    格式字符串是NSString的子类,或者某个类别在某个地方导致在writeWithFormat()期间调用writeWithFormat()。自定义日志记录代码有时会这样做 - 编写一些通用的自定义日志记录代码非常容易,这些代码可以很好地回调自身。去过也做过。很多次,可悲的是。

  • a perversity in memory is causing the recursion; corrupted object or something.

    内存中的异常导致递归;腐败的物体或其他东西。

Both a bit of a grasp at straws. Post the entire crash report.

对稻草都有点把握。发布整个崩溃报告。


Two versions of the bundle swizzling the runtime... all bets are off until the crash is reproduced in the face of only one of those bundles. I'd bet that is the problem.

捆绑的两个版本在运行时间内进行了调整...所有的赌注都关闭,直到崩溃再次出现在其中一个捆绑包中。我敢打赌这就是问题所在。

I also bet that there is some related console spew.

我还打赌,有一些相关的控制台喷出。