使用无效套接字调用boost::asio::write()使我的Blackberry 10应用程序崩溃
Calling boost::asio::write() with a non-valid socket crashed my Blackberry 10 application
这篇文章讲述了最近一个软件项目中遇到的一个技术问题,并让读者从来之不易的问题解决方案中受益。
背景
在我的公司,我是一个内部库的实现者和维护者,该库使用Boost asio("ASynchronous I/O")套接字框架来实现套接字上的跨平台数据传输。最近,一位同事向我提出了以下问题:如果在文件传输操作中随意关闭Wi-Fi路由器,她的黑莓10应用程序在几秒钟内崩溃,该应用程序链接并使用了我的库。
在库中启用内置跟踪表明,当库使用无效的套接字(即套接字可能不可用)调用boost::asio::write(boost::asio::ip::tcp::socket*,boost:;asio:;buffer)函数时,发生了崩溃。在write()周围放置try/catch(boost::system::system_error)块无法捕获任何内容——崩溃显然发生在boost中。
由于崩溃仅发生在发布版本中,因此我们无法使用调试器。
技术信息
- QNX编译器QCC使用GNU 4.6.3
- 使用的Boost版本为1.48.0
以下是编译器的典型命令行调用:
/home/foobar/bbndk/host_10_1_0_238/linux/x86/usr/bin/QCC
-Vgcc_ntoarmv7le
-lang-c++
-x c++
-DLINUX -DQNX -DSUPPORT_LAN -DUSE_SQLITE_FOR_DATABASE
-Wno-psabi -Wno-write-strings
-O3
-DNDEBUG
-fno-strict-aliasing
-fPIC
-I/home/foobar/Libraries/BlackBerry_10/boost_1.48/include
...
-I/home/foobar/Libraries/BlackBerry_10/utfcpp_1.0/include
-o CMakeFiles/Internals.dir/ConfigFileSingleton.cpp.o
-c /home/foobar/myproject_dev/myproject/SDK/Internals/ConfigFileSingleton.cpp
用于定位问题源的步骤
我们编写了一个轻量级的最小化应用程序,试图用更少的代码来重现这个问题,首先使用原始套接字,然后使用Boost的ASIO。如果发生崩溃,我们可以假设问题不是由我们的专有库引起的。不幸的是,这次崩溃无法重现,这让我们怀疑我们的图书馆出了问题。
我们编写了一个轻量级跟踪框架,用于Boost的ASIO头文件,检测与该问题相关的函数。该框架在进入和退出这些函数时输出一个字符串,使我们也能够跟踪变量的值。
使用跟踪框架,我们能够证明崩溃发生在boost::throw_exception()模板函数中(删除了不相关的#ifdef'd代码)。当系统级写入操作在"断开的管道"上失败时,Boost调用此函数:
template<class E> BOOST_ATTRIBUTE_NORETURN inline void throw_exception( E const & e )
{
//All boost exceptions are required to derive from std::exception,
//to ensure compatibility with BOOST_NO_EXCEPTIONS.
throw_exception_assert_compatibility(e);
throw enable_current_exception(enable_error_info(e));
}
插入跟踪并将"throw"语句拆分为单独的语句,向我们证明了崩溃是在抛出异常对象的过程中发生的。很有可能,在抛出异常时,堆栈的展开出现了严重问题。
解决方案
一旦我们意识到这很可能是一个编译器错误,而不是应用程序级别的错误,我们就检查了用于构建库的编译器选项。我们排除了内存损坏的可能性,因为Boost内部代码可能足够坚固和健壮。曾经我们认为发布模式优化可能是罪魁祸首,但距离解决方案的距离很短:将优化级别从-O3降低到-O2。
一旦我们这样做了,崩溃就烟消云散了。
此后,我们修改了QNX工具链中的Blackberry.cmake文件,使用"-O2"而不是原始的"-O3":
SET(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG")
#SET(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
. . .
SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG -fno-strict-aliasing -fPIC")
#SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -fno-strict-aliasing -fPIC")
鉴于此次碰撞,建议谨慎使用"-O3"。我们的minimum应用程序之所以没有重现这个问题,是因为它是用优化级别2而不是3编译的。
我们正在寻找一个SSCCE提交给QNX和/或GNU团队。
完整的解决方案描述可以在上面的问题中找到。根据Dale的指针,我在这里对解决方案进行了简要解释。
崩溃是由于使用默认的优化级别3为Blackberry编译Boost。一旦我们将最佳化水平从-O3降至-O2,崩溃就烟消云散了。
此后,我们修改了QNX工具链中的Blackberry.cmake文件,使其使用"-O2",而不是原始的"-O3"。鉴于此次碰撞,建议谨慎使用"-O3"。
- 我的神经网络不起作用 [XOR 问题]
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 我的字符计数代码计算错误.为什么
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- cmake在我的项目中所需的所有静态库都不成功
- 为什么我的代码在输出中增加了93天
- 我的简单if-else语句是如何无法访问的代码
- 为什么我的for循环不能正确获取argv
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- 0-1背包代码中的错误.我的代码中有什么错误
- 当我的阵列太大时出现分段错误
- 如何确认我的constexpr表达式实际上已经在编译时执行
- 为什么二进制搜索在我的测试中不起作用
- 如何指定我希望我的LIB链接到的DLL文件?-Visual Studio 2019
- 我的代码中有错误吗?使用BGI图形的C++代码对我不起作用
- 当我在main中声明了我的2d数组时,为什么我的程序会退出
- OpenGL在启用深度测试时不会丢弃我的碎片
- 为什么我的 std::ref 无法按预期工作?
- clang整洁10忽略了我的NOLINT命令
- 使用无效套接字调用boost::asio::write()使我的Blackberry 10应用程序崩溃