奇怪的内存问题:线程1:EXC_BAD_ACCESS(代码= 2,地址= 0x16d09aa00)

问题描述 投票:0回答:1

崩溃描述

最近,我在一个iOS / Swift项目中遇到了一个非常奇怪的内存问题。我真的不确定发生了什么,觉得它描述起来也不太容易,但是我还是会尽力而为。

它的基本行为如下:

  • 在特定代码库中,崩溃总是在同一位置发生(可100%重现)
  • 更改代码库可以解决问题,但也可能在其他地方弹出
  • 仅在真实设备上发生崩溃,而在模拟器内部则不会发生崩溃

当前应用程序因以下错误而崩溃(3次不同的运行结果:

线程1:EXC_BAD_ACCESS(代码= 2,地址= 0x16d09aa00)

线程1:EXC_BAD_ACCESS(代码= 2,地址= 0x16af46a00)

线程1:EXC_BAD_ACCESS(代码= 2,地址= 0x16d526a00)


关于内存地址的理由

WWDC会话

我在WWDC 2018上发现了一个有趣的会话(Understanding Crashes and Crash Logs),其中一个人指出,有时可能可以从特定的内存地址中获取更多信息,从而导致崩溃。

不幸的是,它在我的应用程序中崩溃的地址有些完全不同,但是也许我们仍然可以从中获得线索吗?至少有趣的是,它们都非常相似,不是吗?

由于启用了诊断选项而进行的更改

进一步调查显示,前2个字节(16)始终保持不变,其后是4个随机字节,后跟3个字节(a00)。激活诊断(例如ASan或Scribble)时,后3个字节会更改(例如3a0或9e0)。但这也许只是一种转变,因为添加了更多的“调试内容”吗?我真的不是那个“记忆小伙”,但只想提供我注意到的任何东西。


尝试“诊断选项”

我尝试了不同的诊断选项(来自方案),但是它们都没有以任何方式真正改变崩溃或提供了更多信息。

1。涂抹

崩溃不引用0xAA或0x55,因此使用Scribble没什么可捕捉的吗? (Xcode - scribble, guard edges and guard malloc

2。 Malloc保护边缘

也没有注意到使用此方法有任何区别。

3。僵尸

使用此guide

malloc_info --type 0x16b15e9c0

错误:错误:尝试将堆栈放入不可读的存储器中,地址为:0x16b15e920。

4。 ASan

使用ASan只是将以下条目放在堆栈跟踪的顶部。不幸的是,我没有发现与此相关的任何帮助。

#0 0x0000000109efbf60 in __asan_alloca_poison ()

5。 TSan

在真实设备上不可用(仅在此处出现崩溃)


递归/ BOF?

这可能是递归时间太长,还是另一种堆栈/堆缓冲区溢出?但似乎实际设备和模拟器上的堆栈大小与524288字节(来自Thread.main.stackSize)完全相同。

因此,由于它不会在模拟器中崩溃,因此它不是BOF吗?还是架构差异太大,无法在此处得出这样的结论?


拆卸

我也尝试过“拆卸”。

disassemble -a 0x16d09aa00

错误:找不到地址0x16d09aa00的函数范围

disassemble -frame

但是我的组装技能真的很落后,因此从这些信息中我什么也找不到。


需要帮助

如您所见,我真的没有想法了。崩溃真的很奇怪,或者我只是没有足够的知识/技能来使用上面的工具,所以我无法进一步弄清这些问题的起因。

无论哪种方式,任何帮助,提示,想法或任何可能指向我正确方向的东西,我们都表示赞赏!

谢谢,伙计们。


2020年5月19日更新

[我完全忘了提及,我们在应用程序中大量使用ReSwift,而且崩溃似乎与我们在那里使用中间件的方式有关。

我也已经与那里的开发人员联系:github.com/ReSwift/ReSwift/issues/271

终于有了一些代码。不幸的是,我不能共享所有应用程序代码(这可能是必要的!?),也不想让您过多地获取太多代码。

当前问题

线程1:EXC_BAD_ACCESS(代码= 1,地址= 0x16ed82da0)

UserAccountMiddleware.swift

注意:使用这些DispatchQueue.main.async实际上会使崩溃消失。它们确实打破了当前周期,所以也许存在某种递归或时序问题?

func userAccountMiddleware() -> Middleware<AppState> {
    return { dispatch, getState in
        return { next in
            return { action in
                switch action {
                case _ as ReSwiftInit:
//                    DispatchQueue.main.async {
                        dispatch(UserAccountSetAuthToken(authToken: Defaults.customerAuthToken))
                        dispatch(UserAccountSetAvatar(index: Defaults.avatarIndex))
//                    }
                    if let data = Defaults.customer,
                        let customer = try? JSONDecoder().decode(Customer.self, from: data) {
//                        DispatchQueue.main.async {
                            dispatch(UserAccountSetCustomerLoggedIn(customer: customer))
//                        }
                    }

                // [...]

                default:
                    break
                }

                next(action)
            }
        }
    }
}

ReSwift Store.swift

    // [...]
    open func _defaultDispatch(action: Action) {
        guard !isDispatching else {
            raiseFatalError(
                "ReSwift:ConcurrentMutationError- Action has been dispatched while" +
                " a previous action is action is being processed. A reducer" +
                " is dispatching an action, or ReSwift is used in a concurrent context" +
                " (e.g. from multiple threads)."
            )
        }

        isDispatching = true
        let newState = reducer(action, state) // Thread 1: EXC_BAD_ACCESS (code=1, address=0x16ed82da0)
        isDispatching = false

        state = newState
    }
    // [...]

Xcode控制台:

(lldb) po state
error: warning: couldn't get required object pointer (substituting NULL): Couldn't load 'self' because its value couldn't be evaluated

error: Trying to put the stack in unreadable memory at: 0x16d95ad00.

汇编器(崩溃的最后一步):

myapp`type metadata accessor for GlobalState:
    0x101f6ac10 <+0>:  sub    sp, sp, #0x30             ; =0x30 
->  0x101f6ac14 <+4>:  stp    x29, x30, [sp, #0x20] // Thread 1: EXC_BAD_ACCESS (code=1, address=0x16ed82da0)
    0x101f6ac18 <+8>:  adrp   x8, 3620
    0x101f6ac1c <+12>: add    x8, x8, #0x148            ; =0x148 
    0x101f6ac20 <+16>: ldr    x8, [x8]
    0x101f6ac24 <+20>: mov    x9, #0x0
    0x101f6ac28 <+24>: mov    x1, x8
    0x101f6ac2c <+28>: str    x0, [sp, #0x18]
    0x101f6ac30 <+32>: str    x1, [sp, #0x10]
    0x101f6ac34 <+36>: str    x9, [sp, #0x8]
    0x101f6ac38 <+40>: cbnz   x8, 0x101f6ac54           ; <+68> at <compiler-generated>
    0x101f6ac3c <+44>: adrp   x1, 2122
    0x101f6ac40 <+48>: add    x1, x1, #0x1dc            ; =0x1dc 
    0x101f6ac44 <+52>: ldr    x0, [sp, #0x18]
    0x101f6ac48 <+56>: bl     0x102775358               ; symbol stub for: swift_getSingletonMetadata
    0x101f6ac4c <+60>: str    x0, [sp, #0x10]
    0x101f6ac50 <+64>: str    x1, [sp, #0x8]
    0x101f6ac54 <+68>: ldr    x0, [sp, #0x8]
    0x101f6ac58 <+72>: ldr    x1, [sp, #0x10]
    0x101f6ac5c <+76>: str    x0, [sp]
    0x101f6ac60 <+80>: mov    x0, x1
    0x101f6ac64 <+84>: ldr    x1, [sp]
    0x101f6ac68 <+88>: ldp    x29, x30, [sp, #0x20]
    0x101f6ac6c <+92>: add    sp, sp, #0x30             ; =0x30 
    0x101f6ac70 <+96>: ret     
ios swift xcode exc-bad-access reswift
1个回答
0
投票

我现在几乎可以百分百确定这是堆栈溢出,因为我(最终)设法在一个小演示项目中重现了这一点:https://github.com/d4rkd3v1l/ReSwift-StackOverflowDemo

现在,我将为可能遇到此问题或类似问题的其他任何人提供更多详细信息和解决方案。

iOS(自iOS 13起)的堆栈大小为512kb,应同时适用于设备和模拟器。为什么我说“应该”?因为在模拟器上几乎可以肯定它有些不同,因为我在那里没有看到那些崩溃。因此,也许Thread.main.stackSize仅告诉512kb,但实际上更大吗? IDK 🤷‍♂️


这里有一些指标,您可能会遇到相同的问题:

  • 您收到EXC_BAD_ACCESS崩溃,代码为1或2 **。 崩溃发生在高内存地址中,或者至少完全超出了您的应用程序/堆栈其余部分通常所“驻留”的位置。在我的情况下,例如0x16d95ad00
  • 减少您放在堆栈中的内容(值类型,例如非常大的结构)或将调用堆栈分解成较小的部分(例如,调度异步)以使堆栈有一些“呼吸时间”可以防止此崩溃。

并且在后者,我们已经在该问题的解决方案中间。由于堆栈大小无法增加(甚至可能不应该增加),因此您必须减少放置在此处的负载,如第二点所述。

至少这是我们可能追求的解决方案。 🤞


**至少对于主线程是正确的,其他线程可能有所不同。

**我认为代码0有点是空指针异常,因此不适用于此处。如果我对此有误,请纠正我。

© www.soinside.com 2019 - 2024. All rights reserved.