将char数组强制转换为结构时出现Segfault

Segfault when casting char array to struct

本文关键字:结构 Segfault 转换 char 数组      更新时间:2023-10-16

当我使用g++4.7编译并运行时,我试图将char数组强制转换为结构,并接收segfault,但当我使用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 }

不能保证结构的实际大小是结构中每个成员的标称大小之和。

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

以下是更多详细信息:

为什么不是';t结构的sizeof等于每个成员的sizeof之和?

设置

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

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

问题

然后您将看到::strncpy(sendMsg.body, input.c_str(), MESSAGE_MAX_LENGTH - 28);,它可能会写入比空间多得多的字符!这可以保持写入超过sendMsg的末尾,以及堆栈上它旁边发生的任何事情,这可能因编译器而异。也许你的叮当汇编&输入越来越幸运(尽管仍然不正确(。

一旦覆盖了无辜的堆栈变量,像segfault这样的Bad Things(tm(随时都可能发生。

预防

假设您不需要以null终止的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));

请记住,这可能仍然是丢弃信息,而不是null终止,但至少它不会践踏您的堆栈。