使用无效套接字调用boost::asio::write()使我的Blackberry 10应用程序崩溃

Calling boost::asio::write() with a non-valid socket crashed my Blackberry 10 application

本文关键字:我的 Blackberry 崩溃 应用程序 asio 无效 套接字 调用 boost write      更新时间:2023-10-16

这篇文章讲述了最近一个软件项目中遇到的一个技术问题,并让读者从来之不易的问题解决方案中受益。

背景

在我的公司,我是一个内部库的实现者和维护者,该库使用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"。