【问题标题】:Invalid free while performing a std::string assign with -O2 set in g++使用 g++ 中设置的 -O2 执行 std::string 分配时无效的空闲
【发布时间】:2010-02-22 22:09:05
【问题描述】:

在我打开另一个问题之前,这个问题与我几天前打开的另一个问题有关:

C++ Program Always Crashes While doing a std::string assign

在根据一些答案进一步调查后,我提出了更多问题和更多信息,可能有助于调试问题。

所以这里...

有问题的代码:

bool peopleSProtocol::SignalProtocolCreated(BaseProtocol *pProtocol,
        Variant customParameters) {

   LOG("peopleSProtocol::SignalProtocolCreated");

   SetOutboundConnectParameters(customParameters);

   // Tie up the peopleSProtocol Instance to the BaseOutboundStream Instance.
   BaseClientApplication *pApplication = ClientApplicationManager::FindAppByName("peoplestreamer");

   std::string lStreamName;
   printf("1b [%d] [%s]", (int) lStreamName.size(), lStreamName.c_str()); // line 217

   SetApplication(pApplication); // line 218

   printf("1c [%d] [%s]", (int) lStreamName.size(), lStreamName.c_str()); // line 219

   BaseRTAppProtocolHandler *pProtocolHandler = (BaseRTAppProtocolHandler *)pApplication->GetProtocolHandler(PT_OUTBOUNDRT);

   printf("1d [%d] [%s]", (int) lStreamName.size(), lStreamName.c_str());

   uint32_t protocolId = customParameters["customParameters"]["outboundRTProtocolId"];
   uint32_t streamId = customParameters["customParameters"]["streamId"];

   printf("1d [%d] [%s]", (int) lStreamName.size(), lStreamName.c_str());

   BaseOutboundStream *lpBaseOutboundStream = (BaseOutboundStream*)pProtocolHandler->FindByProtocolIdById(protocolId,streamId);

   printf("1e [%d] [%s]", (int) lStreamName.size(), lStreamName.c_str());

   RegisterOutboundStream(lpBaseOutboundStream);

   printf("2 [%d] [%s]", (int) lStreamName.size(), lStreamName.c_str());

   // Get the address of our peer (the RT peer, NOT the peopleS peer)...

   int32_t lRTClientFd = lpBaseOutboundStream->GetProtocol()->GetIOHandler()->GetFd();

   struct sockaddr_in lPeerAddressStruct;
   int lPeerLength = sizeof(lPeerAddressStruct);
   getpeername(lRTClientFd,(sockaddr*)&lPeerAddressStruct,(socklen_t*)&lPeerLength);

   string lPeerIpAddressString = inet_ntoa(lPeerAddressStruct.sin_addr);

   uint16_t lPeerPort = ntohs(lPeerAddressStruct.sin_port);

   printf("3 [%d] [%s]", (int) lStreamName.size(), lStreamName.c_str());

   // Get the name of the file in accordance with the RT spec...

   GetStreamName(STR(M_INVOKE_PARAM(customParameters,1)), lStreamName); // line 247  
   return SendPlayCommand(lStreamName,lPeerIpAddressString,lPeerPort);
}

bool peopleSProtocol::GetStreamName(const char* pRawPath, std::string &pStreamName)
{
   vector<string> parts = split(pRawPath,"/"); // line 260
   pStreamName = parts.back(); // line 261

   parts = split(pStreamName.c_str(),":");
   pStreamName = parts.back();
   return true;
}

#define ADD_VECTOR_END(v,i) (v).push_back((i))

vector<string> split(string str, string separator) {
    vector<string> result;

    string::size_type position = str.find(separator);
    uint32_t separatorLength = separator.length();

    while (position != str.npos) {
        string temp = str.substr(0, position);
        ADD_VECTOR_END(result, temp);
        str = str.substr(position + separatorLength);
        position = str.find(separator);
    }
    ADD_VECTOR_END(result, str);
    return result;
}

GDB 回溯:

(gdb) b 217
Breakpoint 3, peopleSProtocol::SignalProtocolCreated (this=0x3c52020, pProtocol=<value optimized out>, customParameters=@0x413f9eb0) at peopleRTstreamer/src/peoplesprotocol.cpp:217
217    printf("1b [%d] [%s]", (int) lStreamName.size(), lStreamName.c_str());
(gdb) p lStreamName 
$1 = {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x3c52020 "\220R̪�*"}}
(gdb) c
[DEBUG] cpeopleTcpConnection - Connected to 127.0.0.1:37255
[DEBUG] cpeopleTcpConnection - Connected from server address : 127.0.0.1
[New Thread 1096964416 (LWP 10941)]
Breakpoint 4, peopleSProtocol::SignalProtocolCreated (this=0x3c52020, pProtocol=<value optimized out>, customParameters=@0x413f9eb0) at peopleRTstreamer/src/peoplesprotocol.cpp:218
218    SetApplication(pApplication);
(gdb) p lStreamName 
$2 = {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x2abbad098e78 ""}}
(gdb) c
Breakpoint 5, peopleSProtocol::SignalProtocolCreated (this=0x3c52020, pProtocol=<value optimized out>, customParameters=@0x413f9eb0) at peopleRTstreamer/src/peoplesprotocol.cpp:219
219    printf("1c [%d] [%s]", (int) lStreamName.size(), lStreamName.c_str());
(gdb) p lStreamName 
$3 = {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x2abbad098e78 ""}}
(gdb) c
[New Thread 1097099584 (LWP 11010)]
1b [0] []1c [0] []1d [0] []1d [0] [][DEBUG] peopleStreamingServer.RTServer - Opening connection from 127.0.0.1
[DEBUG] peopleStreamingServer.RTServer - Using server IP address 127.0.0.1
Breakpoint 6, peopleSProtocol::SignalProtocolCreated (this=0x3c52020, pProtocol=<value optimized out>, customParameters=@0x413f9eb0) at peopleRTstreamer/src/peoplesprotocol.cpp:247
247    GetStreamName(STR(M_INVOKE_PARAM(customParameters,1)), lStreamName);
(gdb) p lStreamName 
$4 = {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x2abbad098e78 ""}}
(gdb) c
Breakpoint 7, peopleSProtocol::GetStreamName (this=0x3c52020, pRawPath=0x3c40808 "RT://127.0.0.1/mp4:popeye.mp4", pStreamName=@0x413f9e10) at peopleRTstreamer/src/peoplesprotocol.cpp:260
260    vector<string> parts = split(pRawPath,"/");
(gdb) p pStreamName 
$5 = (string &) @0x413f9e10: {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, 
    _M_p = 0x2abbad098e78 ""}}
(gdb) c
*** glibc detected *** /home/ml01/t-live/TEngine/StreamingServer/.libs/peoplestreamingserver: free(): invalid pointer: 0x00002abbad098e60 ***

======= Backtrace: =========
/lib64/libc.so.6[0x3560871634]
/lib64/libc.so.6(cfree+0x8c)[0x3560874c5c]
/home/ml01/t-HEAD/gcc/4.2.4/lib/../lib64/libstdc++.so.6(_ZNSs6assignERKSs+0x90)[0x2abbace3fd20]
/home/ml01/usr/local/lib/RTserver/libpeopleRTstreamer.so(_ZN12peopleSProtocol13GetStreamNameEPKcRSs+0x9a)[0x2aaaaaabd16a]
/home/ml01/usr/local/lib/RTserver/libpeopleRTstreamer.so(_ZN12peopleSProtocol21SignalProtocolCreatedEP12BaseProtocol7Variant+0x2e4)[0x2aaaaaabd894]
/home/ml01/usr/local/lib/RTserver/libpeopleRTstreamer.so(_ZN12TCPConnectorI12peopleSProtocolE7OnEventER11epoll_event+0x1a6)[0x2aaaaaabf046]
/home/ml01/t-HEAD/cRTserver/20091229/lib/libthelib.so(_ZN16IOHandlerManager5PulseEv+0x4a6)[0x2abbad425620]
/home/ml01/usr/local/lib/libpeopleRT.so.0(_ZN3people15cRTServerLoop19RTProtocolHandlerEv+0x55)[0x2abba2d2df45]
/home/ml01/usr/local/lib/libpeopleRT.so.0(_Z19HandleTrafficThreadPv+0x9)[0x2abba2d2e009]
/home/ml01/t-HEAD/glib/2.18.0/lib/libglib-2.0.so.0[0x2abbabc84394]
/lib64/libpthread.so.0[0x35614062f7]
/lib64/libc.so.6(clone+0x6d)[0x35608d1b6d]

(gdb) where
#0  0x0000003560830155 in raise () from /lib64/libc.so.6
#1  0x0000003560831bf0 in abort () from /lib64/libc.so.6
#2  0x000000356086a38b in __libc_message () from /lib64/libc.so.6
#3  0x0000003560871634 in _int_free () from /lib64/libc.so.6
#4  0x0000003560874c5c in free () from /lib64/libc.so.6
#5  0x00002abbace3fd20 in std::string::assign (this=0x413f9e10, __str=<value optimized out>) at /home/ml01/ThirdParty/sources/gcc-4.2.4/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:238
#6  0x00002aaaaaabd16a in peopleSProtocol::GetStreamName (this=<value optimized out>, pRawPath=0x3c40808 "RT://127.0.0.1/mp4:popeye.mp4", pStreamName=@0x413f9e10)
    at /home/ml01/t-HEAD/gcc/4.2.4/lib/gcc/x86_64-pc-linux-gnu/4.2.4/../../../../include/c++/4.2.4/bits/basic_string.h:491
#7  0x00002aaaaaabd894 in peopleSProtocol::SignalProtocolCreated (this=0x3c52020, pProtocol=<value optimized out>, customParameters=@0x413f9eb0) at peopleRTstreamer/src/peoplesprotocol.cpp:247
#8  0x00002aaaaaabf046 in TCPConnector<peopleSProtocol>::OnEvent (this=0x3c40aa0, event=<value optimized out>) at /home/ml01/t-HEAD/cRTserver/20091229/include/cRTserver/netio/epoll/tcpconnector.h:90
#9  0x00002abbad425620 in IOHandlerManager::Pulse () at /home/ml01/ThirdParty/sources/cRTserver/sources/thelib/src/netio/epoll/iohandlermanager.cpp:260
#10 0x00002abba2d2df45 in people::cRTServerLoop::RTProtocolHandler (this=0x3c4c180) at src/RTServerLoop.cpp:77
#11 0x00002abba2d2e009 in HandleTrafficThread (ipData=0x60bc) at src/RTServerLoop.cpp:39
#12 0x00002abbabc84394 in g_thread_create_proxy (data=0x3c4e060) at gthread.c:635
#13 0x00000035614062f7 in start_thread () from /lib64/libpthread.so.0
#14 0x00000035608d1b6d in clone () from /lib64/libc.so.6

因此,为了解释上述情况,在字符串分配期间,崩溃发生在第 261 行(有关行号,请参见代码中的 cmets)。出于某种原因,它试图释放无效内存,但我不确定为什么?它试图释放的地址不是我认为是在第 217 行分配的空字符串的地址(_M_p = 0x3c52020 被设置为 _M_p = 0x2abbad098e78 即空字符串),而是其他一些距离不太远的地址。

任何想法可能导致此问题?我想知道这是否可能是一个优化问题,因为它不会发生在 -O0 中,我已经通过一系列测试运行了代码,试图找到一系列可能导致这种奇怪行为的内存损坏问题...... .空手而来!

另一个有趣的注意事项是,如果我设置 std::string lStreamName = "";而不是 std::string lStreamName;程序不再崩溃???任何想法可能导致这种情况?为什么这个字符串的简单初始化可以消除崩溃?

谢谢!

【问题讨论】:

  • 清理代码会不会很痛苦,所以不都是双倍行距的? chrome 不允许我选择每个 div 的内容。
  • 什么是 split()?它不会调用 strtok()?
  • 我添加了拆分功能!对于那个很抱歉!它不使用 strtok()
  • 我还清理了一些代码,希望对您有所帮助!
  • 许多内存损坏的情况在远离问题所在的地方有明显的影响,许多稍微不正确的程序会偶然在一个优化级别而不是另一个优化级别上正确运行。 (更积极的优化通常不会在正确的程序中产生错误。)这可能很困难。

标签: c++ optimization crash g++ stdstring


【解决方案1】:

如果不能保证 split 不会返回空向量,我会添加一个检查它是否为空。否则将空向量的 back() 分配给字符串可能会导致您收到的行为。

bool GetStreamName(const char* pRawPath, std::string& pStreamName)
{
    std::vector<std::string> parts = split(pRawPath,'/'); // line 260
    if( parts.size() == 0 )
    {
        cout << "Cannot parse '" << pRawPaths << "'" << endl;
        return false;
    }
    pStreamName = parts.back(); // line 261

    parts = split(pStreamName.c_str(),':');
    pStreamName = parts.back();
    return true;
}

【讨论】:

  • 当我单步执行代码时,拆分函数返回一个不为空且包含正确字符串的向量,因此这似乎不是问题,但 +1 因为这是一个很好的检查在代码中有!
  • 好收获! IIRC,在空向量上调用 back() 会导致未定义的行为。 +1
  • 我更改了代码以添加您提到的检查,但我从未返回空向量,所以这不是问题。但是,我测试了向量为空并且程序在 -O0 和 -O2 中都崩溃的情况。好发现!奇怪的是,当传递给此函数的字符串按以下方式初始化时,程序不再崩溃: std::string lStreamName = ""; ???我不知道为什么?有什么想法吗?
  • 老实说,我错过了第一次阅读时只有 -O2 build crash 的部分。您可以尝试代替字符串引用来返回字符串吗?我知道您的方式是有效的,但我想知道编译器是否可以以某种奇怪的方式对其进行优化。在 gdb 中,您还可以使用 'step' 而不是 'c'(继续),并逐步查看导致崩溃的原因。
【解决方案2】:

您应该尝试使用一个独立的示例来重现崩溃。使用您提供给我们的信息,我测试了以下代码。

请注意,我需要一个 split 函数。不知道你用的是什么,我从this question拿了一个。如果您使用自己编写的 boost::splitsplit 函数,请将其包含在您的问题中。

#include <iostream>
#include <sstream>
#include <string>
#include <vector>

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
    std::stringstream ss(s);
    std::string item;
    while(std::getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}

std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> elems;
    return split(s, delim, elems);
}

bool GetStreamName(const char* pRawPath, std::string& pStreamName)
{
    std::vector<std::string> parts = split(pRawPath,'/'); // line 260
    pStreamName = parts.back(); // line 261

    parts = split(pStreamName.c_str(),':');
    pStreamName = parts.back();
    return true;
}

int main()
{
    std::string streamName;
    GetStreamName("/some:weird/path/of:x/a:/stream:foo", streamName);
    std::cout << streamName << std::endl;
}

尝试运行此代码(使用 -O2、使用您能想到的任何 g++ 优化、使用 valgrind 等)并查看它是否再次崩溃。

使用一个简单的、独立的示例,您可以确定崩溃的根源是您的代码中的错误还是其他任何原因(例如冲突的 C++ 库,就像您在上一个问题中一样)。

【讨论】:

  • 我用我的 split 函数版本试过这个,我没有崩溃,也没有在 valgrind 中收到警告,所以代码似乎不是直接的问题。我认为这可能是链接或 .so 问题,但我真的不确定?
  • 尝试在 valgrind 中再次运行您的代码。如果 valgrind 警告你无效的内存访问,你已经找到了你的错误。但是,如果您仍然遇到stackoverflow.com/questions/2303740/… 中描述的问题,那么很可能问题出在您的环境中,而不是在您的代码中。
  • 如果可以,请尝试在另一个系统上编译和运行您的崩溃代码(可能使用随发行版安装的默认编译器),看看您是否仍然遇到崩溃。
  • 我已经在 Linux RH 4.2、4.4 和 Ubuntu 9.04 上尝试过,但我仍然遇到问题,但软件包和库非常相似,并非所有都是发行版附带的版本所以那里可能有问题,但我不知道如何收敛!
  • 您是否至少在一种情况下使用了发行版的 g++ 编译器? Ubuntu 9.04 包括 gcc 4.3.3,RHEL 4.2 有 gcc 4.0.1,而 RHEL 4.4 使用 gcc 4.1.0。您是在同一台机器上还是在不同的机器上进行这些测试?
猜你喜欢
  • 2023-02-11
  • 1970-01-01
  • 2011-07-26
  • 1970-01-01
  • 2013-09-10
  • 2014-09-22
  • 2011-01-19
  • 2012-10-02
  • 1970-01-01
相关资源
最近更新 更多