【发布时间】:2023-03-22 13:38:01
【问题描述】:
我在调试一个 Android 应用程序的崩溃时遇到了以下几行:
void myobject::save(std::string toSave) {
...
}
...
std::map<std::string, std::string> dict;
...
myobject::save(dict.find("username")->second);
生成的程序集:
02F05DFC bl _ZNKSt6__ndk16__treeINS_12__value_typeINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES7_EENS_19__map_value_compareIS7_S8_NS_4lessIS7_EELb1EEENS5_IS8_EEE4findIS7_EENS_21__tree_const_iteratorIS8_PNS_11__tree_nodeIS8_PvEEiEERKT_
3096 str r6, [sp, #0x108 + var_48]
0146 mov r1, r0
CDE92E66 strd r6, r6, [sp, #0x108 + var_50]
11F81C2F ldrb r2, [r1, #0x1c]!
12F0010F tst.w r2, #0x1
06D1 bne loc_THERE
loc_FIRST:
D1E90002 ldrd r0, r2, [r1]
8968 ldr r1, [r1, #0x8]
3091 str r1, [sp, #0x108 + var_48]
CDE92E02 strd r0, r2, [sp, #0x108 + var_50]
04E0 b loc_HERE
loc_THERE:
D0E90821 ldrd r2, r1, [r0, #0x20]
2EA8 add r0, sp, #0xb8_ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcj
FDF743FD bl _ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcj
loc_HERE:
2EA9 add r1, sp, #0xb8
2046 mov r0, r4
04F047FB bl _ZN6myobject11saveENSt6__ndk112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE
我发现了错误。当 find() 返回 end() 迭代器时会发生崩溃。但是,有趣的是,崩溃发生在 new() 内部(在 basic_string::__init() 内部完成)。当 find() 返回 end() 迭代器时,似乎 end() (end()->second) 的取消引用不会崩溃,但是当它将 end()->second 转换为 std::string 时会崩溃() 在传递给 myobject::save() 之前。
我试图查看 libcxx 实现,但没有找到我想要的。有谁知道 loc_FIRST 和 loc_THERE 两个块的用途是什么?为什么要检查 *(r1+0x1c) == 0x1?有人能解释一下汇编代码中“->second”的位置吗?
【问题讨论】:
-
专业提示:在任何返回迭代器的函数上,它可能是
end迭代器,在使用它之前,请始终检查end的返回值, -
end()->second表现出未定义的行为。表现出未定义行为的程序的确切故障模式并不是那么有趣。你为什么在乎?但如果你坚持:end()实际上是一个指向垃圾的指针。然后将该垃圾传递给std::string复制构造函数,因为save()按值获取其参数。复制构造函数尝试实际使用该垃圾的内容,然后崩溃。 -
c++filt刚刚创造了一个损坏符号名称最不可读扩展的新记录:std::__ndk1::__tree_const_iterator<std::__ndk1::__value_type<std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> >, std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > >, std::__ndk1::__tree_node<std::__ndk1::__value_type<std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, ...停在那里,因为它对于另外 1000 个字符的评论来说太长了! -
(来自第一个
bl指令,它返回一个指向某种对象的指针,该对象在+0x1c有一个成员,您的代码正在检查它的低位。注意它是@987654333 @,不是cmp,所以结果只取决于ldrb加载的字节的低位,而不是全部)。 -
我认为它正在做一个小字符串优化(SSO)。
loc_FIRST情况是字符串包含在 SSO 缓冲区中的情况,并且只是将字符串的 12 个字节直接复制到字符串的堆栈位置。loc_THERE情况是 SSO 处于非活动状态并调用std::string::__init进行分配和其他非 SSO 初始化工作。所以本质上,该分支是std::string构造的一部分,该构造已内联到此代码中(但慢速非 SSO 路径仍然不合时宜)。被检查的位可能表明 SSO 是否对该字符串处于活动状态。
标签: c++ dictionary assembly std libc++