1

当我使用 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 }
4

2 回答 2

1

无法保证您的结构的实际大小将是结构中每个成员的标称大小的总和。

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

以下是更多详细信息:

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

于 2014-04-30T05:42:41.453 回答
0

设置

忽略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)随时可能发生。

预防

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

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

于 2014-04-30T07:21:02.783 回答