【问题标题】:Segfault when casting char array to struct将字符数组转换为结构时出现段错误
【发布时间】:2014-06-16 07:42:09
【问题描述】:

当我使用 g++ 4.7 编译并运行时,我试图将 char 数组转换为结构并接收段错误,但当我使用 clang 3.3 编译并运行程序时却没有。

这是消息结构描述:

typedef struct {
 char code[4];
 char nickname[24];
 struct in_addr addr;
 int port;
 int seqNum;
 char body[MESSAGE_MAX_LENGTH - (sizeof(char) * 28) - (sizeof(int) * 2) - sizeof(struct in_addr)];
} Message;

这是导致段错误的行:

Message *recvMsg = (Message *)packet.buffer;

数据包对象属于以下类型:

class UDPPacket {
     public:
         static const size_t maxBufferSize = MESSAGE_MAX_LENGTH;
         uint8_t buffer[maxBufferSize];
         size_t length;
         struct in_addr addr;
         int port;

         UDPPacket();
         UDPPacket(struct in_addr addr, int port);
         std::string getRemoteAddrString();
         std::string getDataAsString();
 };

这是 gdb 产生的回溯:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff67f5700 (LWP 22753)]
0x00007ffff79787f7 in ?? () from /usr/lib64/libstdc++.so.6
(gdb) backtrace
#0  0x00007ffff79787f7 in ?? () from /usr/lib64/libstdc++.so.6
#1  0x00007ffff7978850 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() () from /usr/lib64/libstdc++.so.6
#2  0x0000000000411cb7 in SendMessage () at /home1/user123/ugh/final-project/main.cpp:189
#3  0x00000000004172df in std::_Bind_simple<void (*())()>::_M_invoke<>(std::_Index_tuple<>) (
    this=0x620310) at /usr/include/c++/4.7/functional:1598
#4  0x000000000041722f in std::_Bind_simple<void (*())()>::operator()() (this=0x620310)
    at /usr/include/c++/4.7/functional:1586
#5  0x00000000004171c8 in std::thread::_Impl<std::_Bind_simple<void (*())()> >::_M_run() (
    this=0x6202f8) at /usr/include/c++/4.7/thread:115
#6  0x00007ffff796f340 in ?? () from /usr/lib64/libstdc++.so.6
#7  0x00007ffff7bc6e0f in start_thread () from /lib64/libpthread.so.0
#8  0x00007ffff70e044d in clone () from /lib64/libc.so.6

当我使用 g++ 而不是 clang 编译时,我不确定为什么会出现段错误?

编辑:这就是 SendMessage() 的样子:

 56 void SendMessage() {
 57     while(true){
 58         std::cout << "> ";
 59         std::string input;
 60         std::getline(std::cin, input);
 61
 62         if(input.length() > 140){
 63             std::cout << "[Message is too long and was not sent]" << std::endl;
 64             continue;
 65         }
 66
 67         Message sendMsg;
 68         ::strncpy(sendMsg.code, "SEND", 4);
 69         ::strncpy(sendMsg.body, input.c_str(), MESSAGE_MAX_LENGTH - 28);
 70
 71         if(isLeader){
 72             // Leader Code
 73             ::strncpy(sendMsg.nickname, lState->name.c_str(), lState->name.length());
 74             sendMsg.addr = lState->ownAddr.addr;
 75             sendMsg.port = lState->ownAddr.port;
 76
 77             sendMsg.seqNum = lState->seqNum;
 78             broadcast(lState->allClients, &sendMsg);
*lines 79 to 187 commented out*
187             lState->seqNum += 1;
188          }
189     } //end of while loop
190 }

【问题讨论】:

  • 您应该向我们展示方法SendMessage()。从跟踪中,我们看到 (#2),这是您编写的最后一个涉及的方法。看起来不像是recvMsg的赋值错误(其实那里不会出错),而是std::string的使用。
  • 回溯没有指向你说的导致段错误的行。
  • 尝试破坏string时出现问题
  • 您的结构没有打包,并且不知道“struct in_addr adde;”的大小,您不能确定不会在结构中间添加填充字节。您对 Message.body 大小的计算可能是错误的。这很可能会导致在您指出的强制转换之后通过有效内存末尾的引用。
  • @user1710707 第 189 行作为结束括号非常有意义:这是调用析构函数的行。

标签: c++ c++11 struct clang g++-4.7


【解决方案1】:

不能保证你的结构体的实际大小是结构体每个成员的名义大小的总和。

由于填充或其他编译器和/或平台特定的问题,实际大小可能更大

这里有更多细节:

Why isn't sizeof for a struct equal to the sum of sizeof of each member?

【讨论】:

    【解决方案2】:

    设置

    忽略sizeof(Message) 可能不是您认为的事实(尽管它可能是!),您将Message sendMsg 放在堆栈上。

    假设标准类型宽度,sendMsg.body 有空间用于[MESSAGE_MAX_LENGTH - 36 - sizeof(in_addr)] 字符。

    问题

    那么您将与 ::strncpy(sendMsg.body, input.c_str(), MESSAGE_MAX_LENGTH - 28); 一起出现,它可能会写入 更多 个字符,超出空间!这可以继续写到sendMsg 的末尾,以及堆栈上它旁边的任何内容,这可能因编译器而异。也许您的 clang 编译和输入很幸运(尽管仍然不正确)。

    一旦你覆盖了无辜的堆栈变量,像段错误这样的坏事(tm)就可能随时发生。

    预防

    假设您不需要以空值结尾的 char 数组(您可能会这样做),请确保您从不复制超出您空间的数据。

    ::strncpy(sendMsg.code, "SEND", sizeof(sendMsg.code));
    ::strncpy(sendMsg.body, input.c_str(), sizeof(sendMsg.body));
    ::strncpy(sendMsg.nickname, lState->name.c_str(), sizeof(sendMsg.nickname));
    

    请记住,这可能仍然是丢弃信息而不是空终止,但至少它不会压垮你的堆栈。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-09
      • 2021-06-28
      • 2020-04-15
      • 2020-08-21
      • 2021-10-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多