【问题标题】:c++ linux send uninitialised sockets memoryc++ linux发送未初始化的sockets内存
【发布时间】:2020-07-28 16:57:11
【问题描述】:

我是新手,我使用的是 debian 10 x64。为什么错误与 SEND 一致?有人可以建议我如何解决这个问题?

send(socket, (char*)m_MsgBuf+sendBytes, std::min(m_MsgSize-sendBytes+2, 1000),

我收到了那个错误日志:

==24819== Thread 3:
==24819== Syscall param socketcall.sendto(msg) points to uninitialised byte(s)
==24819==    at 0x51442B4: __libc_send (send.c:28)
==24819==    by 0x51442B4: send (send.c:23)
==24819==    by 0x1FCFC9: NetworkMessage::WriteToSocket(int) (networkmessage.cpp:117)
==24819==    by 0x231624: Protocol76::flushOutputBuffer() (protocol76.cpp:3574)
==24819==    by 0x22ECF4: Protocol76::sendThingAppear(Thing const*) (protocol76.cpp:3039)
==24819==    by 0x214DE1: Player::onThingAppear(Thing const*) (player.cpp:2165)
==24819==    by 0x1829D0: Game::sendAddThing(Player*, Position const&, Thing const*) (game.cpp:7005)
==24819==    by 0x16789F: Game::placeCreature(Position&, Creature*, int*) (game.cpp:1260)
==24819==    by 0x223995: Protocol76::ConnectPlayer(int*) (protocol76.cpp:66)
==24819==    by 0x208C43: ConnectionHandler(void*) (otserv.cpp:575)
==24819==    by 0x5139F26: start_thread (pthread_create.c:479)
==24819==    by 0x55F12AE: clone (clone.S:95)
==24819==  Address 0x111137f1 is 289 bytes inside a block of size 16,912 alloc'd
==24819==    at 0x4836DEF: operator new(unsigned long) (vg_replace_malloc.c:344)
==24819==    by 0x2086ED: ConnectionHandler(void*) (otserv.cpp:510)
==24819==    by 0x5139F26: start_thread (pthread_create.c:479)
==24819==    by 0x55F12AE: clone (clone.S:95)
==24819== 

代码:

bool NetworkMessage::WriteToSocket(SOCKET socket)
{
    if (m_MsgSize == 0)
        return true;

    m_MsgBuf[0] = (unsigned char)(m_MsgSize);
    m_MsgBuf[1] = (unsigned char)(m_MsgSize >> 8);

    bool ret = true;
    int32_t sendBytes = 0;
    int32_t flags = 0;
    int32_t retry = 0;

    flags = MSG_DONTWAIT; // 2 ?

    do
    {
        int32_t b = send(socket, (char*)m_MsgBuf+sendBytes, std::min(m_MsgSize-sendBytes+2, 1000), flags);

        if(b <= 0)
        {   
            int32_t errnum;

            if(errnum == EWOULDBLOCK) 
            {
                b = 0;
                OTSYS_SLEEP(10);
                retry++;

                if(retry == 10) 
                {
                    ret = false;
                    break;
                }
            }
            else
            {
                ret = false;
                break;
            }
        }
        sendBytes += b;
    }while(sendBytes < m_MsgSize+2);

    return ret;
}


==930== Thread 3:
==930== Conditional jump or move depends on uninitialised value(s)
==930==    at 0x1FCFE7: NetworkMessage::WriteToSocket(int) (networkmessage.cpp:106)
==930==    by 0x208340: ConnectionHandler(void*) (otserv.cpp:427)
==930==    by 0x5139F26: start_thread (pthread_create.c:479)
==930==    by 0x55F12AE: clone (clone.S:95)
==930==
==930== Syscall param socketcall.sendto(msg) points to uninitialised byte(s)
==930==    at 0x51442B4: __libc_send (send.c:28)
==930==    by 0x51442B4: send (send.c:23)
==930==    by 0x1FCFD9: NetworkMessage::WriteToSocket(int) (networkmessage.cpp:100)
==930==    by 0x231650: Protocol76::flushOutputBuffer() (protocol76.cpp:3574)
==930==    by 0x22ED20: Protocol76::sendThingAppear(Thing const*) (protocol76.cpp:3039)
==930==    by 0x214E0D: Player::onThingAppear(Thing const*) (player.cpp:2165)
==930==    by 0x1829D0: Game::sendAddThing(Player*, Position const&, Thing const*) (game.cpp:7005)
==930==    by 0x16789F: Game::placeCreature(Position&, Creature*, int*) (game.cpp:1260)
==930==    by 0x2239C1: Protocol76::ConnectPlayer(int*) (protocol76.cpp:66)
==930==    by 0x208C6F: ConnectionHandler(void*) (otserv.cpp:575)
==930==    by 0x5139F26: start_thread (pthread_create.c:479)
==930==    by 0x55F12AE: clone (clone.S:95)
==930==  Address 0x111198d1 is 289 bytes inside a block of size 16,912 alloc'd
==930==    at 0x4836DEF: operator new(unsigned long) (vg_replace_malloc.c:344)
==930==    by 0x208719: ConnectionHandler(void*) (otserv.cpp:510)
==930==    by 0x5139F26: start_thread (pthread_create.c:479)
==930==    by 0x55F12AE: clone (clone.S:95)
==930==
==930== Conditional jump or move depends on uninitialised value(s)
==930==    at 0x1FCFE7: NetworkMessage::WriteToSocket(int) (networkmessage.cpp:106)
==930==    by 0x231650: Protocol76::flushOutputBuffer() (protocol76.cpp:3574)
==930==    by 0x22ED20: Protocol76::sendThingAppear(Thing const*) (protocol76.cpp:3039)
==930==    by 0x214E0D: Player::onThingAppear(Thing const*) (player.cpp:2165)
==930==    by 0x1829D0: Game::sendAddThing(Player*, Position const&, Thing const*) (game.cpp:7005)
==930==    by 0x16789F: Game::placeCreature(Position&, Creature*, int*) (game.cpp:1260)
==930==    by 0x2239C1: Protocol76::ConnectPlayer(int*) (protocol76.cpp:66)
==930==    by 0x208C6F: ConnectionHandler(void*) (otserv.cpp:575)
==930==    by 0x5139F26: start_thread (pthread_create.c:479)
==930==    by 0x55F12AE: clone (clone.S:95)
==930== Thread 2:
==930== Conditional jump or move depends on uninitialised value(s)
==930==    at 0x1FCFE7: NetworkMessage::WriteToSocket(int) (networkmessage.cpp:106)
==930==    by 0x231650: Protocol76::flushOutputBuffer() (protocol76.cpp:3574)
==930==    by 0x214089: Player::flushMsg() (player.cpp:1867)
==930==    by 0x182338: Game::flushSendBuffers() (game.cpp:6879)
==930==    by 0x17AAB2: Game::checkCreature(unsigned int) (game.cpp:5461)
==930==    by 0x1A4B67: std::mem_fun1_t<void, Game, unsigned int>::operator()(Game*, unsigned int) const (stl_function.h:1284)
==930==    by 0x1A1668: std::binder2nd<std::mem_fun1_t<void, Game, unsigned int> >::operator()(Game* const&)const (binders.h:158)
==930==    by 0x19BCCD: boost::detail::function::void_function_obj_invoker1<std::binder2nd<std::mem_fun1_t<void, Game, unsigned int>>,void, Game*>::invoke(boost::detail::function::function_buffer&, Game*) (function_template.hpp:159)
==930==    by 0x23810D: boost::function1<void, Game*>::operator()(Game*) const (function_template.hpp:768)
==930==    by 0x237FBC: TSchedulerTask::operator()(Game*) (scheduler.h:63)
==930==    by 0x166D2B: Game::eventThread(void*) (game.cpp:1045)
==930==    by 0x5139F26: start_thread (pthread_create.c:479)
==930==
==930== Thread 3:
==930== Syscall param socketcall.sendto(msg) points to uninitialised byte(s)
==930==    at 0x51442B4: __libc_send (send.c:28)
==930==    by 0x51442B4: send (send.c:23)
==930==    by 0x1FCFD9: NetworkMessage::WriteToSocket(int) (networkmessage.cpp:100)
==930==    by 0x231650: Protocol76::flushOutputBuffer() (protocol76.cpp:3574)
==930==    by 0x214089: Player::flushMsg() (player.cpp:1867)
==930==    by 0x182338: Game::flushSendBuffers() (game.cpp:6879)
==930==    by 0x224573: Protocol76::parsePacket(NetworkMessage&) (protocol76.cpp:405)
==930==    by 0x223A3F: Protocol76::ReceiveLoop() (protocol76.cpp:86)
==930==    by 0x208E70: ConnectionHandler(void*) (otserv.cpp:611)
==930==    by 0x5139F26: start_thread (pthread_create.c:479)
==930==    by 0x55F12AE: clone (clone.S:95)
==930==  Address 0x11119f5f is 1,967 bytes inside a block of size 16,912 alloc'd
==930==    at 0x4836DEF: operator new(unsigned long) (vg_replace_malloc.c:344)
==930==    by 0x208719: ConnectionHandler(void*) (otserv.cpp:510)
==930==    by 0x5139F26: start_thread (pthread_create.c:479)
==930==    by 0x55F12AE: clone (clone.S:95)
==930==

我使用这样的代码:

bool NetworkMessage::WriteToSocket(SOCKET socket)
{
    if (m_MsgSize == 0)
        return true;

    m_MsgBuf[0] = (unsigned char)(m_MsgSize);
    m_MsgBuf[1] = (unsigned char)(m_MsgSize >> 8);

    bool ret = true;
    int32_t sendBytes = 0;
    int32_t flags = 0;
    int32_t retry = 0;

    flags = MSG_DONTWAIT; // 2 ?


    while (sendBytes != m_MsgSize) 
    { /* != rather than < */
        int32_t b = send(socket, (char*)m_MsgBuf+sendBytes, std::min(m_MsgSize-sendBytes+2, 1000), flags);

        if(b <= 0)
        {   
            int32_t errnum;

            if(errnum == EWOULDBLOCK) 
            {
                b = 0;
                OTSYS_SLEEP(10);
                retry++;

                if(retry == 10) 
                {
                    ret = false;
                    break;
                }
            }
            else
            {
                ret = false;
                break;
            }
        }
        sendBytes += b;
    }

    return ret;
}

编辑

上次我遇到类似错误时,我需要使用该代码,现在一切正常:sigemptyset(&amp;sigh.sa_mask);memset(&amp;sigh, 0, sizeof(sigh)); 在这里我在我的项目文件中找到了所有 msgBuf 和 msg 大小。你在这里看到什么有趣的东西吗? m_MsgBuf wklejto.pl/828244 m_MsgSize wklejto.pl/828245

编辑

所以我这样做了,在文件 NetworkMessage.cpp 我在 AddString/AddU32/AddU16/AddByte 下添加了这个:

#ifdef CHECKVAL
    checkval(m_MsgBuf+m_ReadPos, stringlen);
#endif  

在顶部我添加了这个文件:

#include "player.h"

#ifdef CHECKVAL
    extern int checkval(unsigned char * buff, int len);
#endif

最后将此代码添加到 player.cpp

#ifdef CHECKVAL
int checkval(unsigned char * buff, int len)
{
   int r = 0;

   buff -= len;
   while (len--)
     r += *buff++;
   return r;
}
#endif

我也将char 更改为unsigned char - bcs 我收到错误invalid conversion from ‘unsigned char*’ to ‘char*’

到END我添加到编译器这个-DCHECKVAL

之后我看到同样的错误

==15290== Thread 3:
==15290== Syscall param socketcall.sendto(msg) points to uninitialised byte(s)
==15290==    at 0x51442B4: __libc_send (send.c:28)
==15290==    by 0x51442B4: send (send.c:23)
==15290==    by 0x1FCFDC: NetworkMessage::WriteToSocket(int) (networkmessage.cpp:105)
==15290==    by 0x231728: Protocol76::flushOutputBuffer() (protocol76.cpp:3574)
==15290==    by 0x22EDF8: Protocol76::sendThingAppear(Thing const*) (protocol76. cpp:3039)
==15290==    by 0x214E99: Player::onThingAppear(Thing const*) (player.cpp:2165)
==15290==    by 0x1829D0: Game::sendAddThing(Player*, Position const&, Thing const*) (game.cpp:7005)
==15290==    by 0x16789F: Game::placeCreature(Position&, Creature*, int*) (game.cpp:1260)
==15290==    by 0x223A99: Protocol76::ConnectPlayer(int*) (protocol76.cpp:66)
==15290==    by 0x208CFB: ConnectionHandler(void*) (otserv.cpp:575)
==15290==    by 0x5139F26: start_thread (pthread_create.c:479)
==15290==    by 0x55F12AE: clone (clone.S:95)
==15290==  Address 0x11116331 is 289 bytes inside a block of size 16,912 alloc'd
==15290==    at 0x4836DEF: operator new(unsigned long) (vg_replace_malloc.c:344)
==15290==    by 0x2087A5: ConnectionHandler(void*) (otserv.cpp:510)
==15290==    by 0x5139F26: start_thread (pthread_create.c:479)
==15290==    by 0x55F12AE: clone (clone.S:95)
==15290==
==15290== Syscall param socketcall.sendto(msg) points to uninitialised byte(s)
==15290==    at 0x51442B4: __libc_send (send.c:28)
==15290==    by 0x51442B4: send (send.c:23)
==15290==    by 0x1FCFDC: NetworkMessage::WriteToSocket(int) (networkmessage.cpp:105)
==15290==    by 0x231728: Protocol76::flushOutputBuffer() (protocol76.cpp:3574)
==15290==    by 0x214115: Player::flushMsg() (player.cpp:1867)
==15290==    by 0x182338: Game::flushSendBuffers() (game.cpp:6879)
==15290==    by 0x22464B: Protocol76::parsePacket(NetworkMessage&) (protocol76.cpp:405)
==15290==    by 0x223B17: Protocol76::ReceiveLoop() (protocol76.cpp:86)
==15290==    by 0x208EFC: ConnectionHandler(void*) (otserv.cpp:611)
==15290==    by 0x5139F26: start_thread (pthread_create.c:479)
==15290==    by 0x55F12AE: clone (clone.S:95)
==15290==  Address 0x111169bf is 1,967 bytes inside a block of size 16,912 alloc'd
==15290==    at 0x4836DEF: operator new(unsigned long) (vg_replace_malloc.c:344)
==15290==    by 0x2087A5: ConnectionHandler(void*) (otserv.cpp:510)
==15290==    by 0x5139F26: start_thread (pthread_create.c:479)
==15290==    by 0x55F12AE: clone (clone.S:95)
==15290==

hmm.. 但是这个函数在第 510 行看起来很有趣ConnectionHandler 有问题吗?

            Protocol76* protocol;
            protocol = new Protocol76(s);

和:

Protocol76::Protocol76(SOCKET s)
{
    OTSYS_THREAD_LOCKVARINIT(bufferLock);

    player = NULL;
    pendingLogout = false;

    windowTextID = 0;
    readItem = NULL;
    this->s = s;
}

完整的ConnectionHandler 并且密码123

enter link description here

文件中的510 行是152 你怎么看?

这样就够了吗?

int checkval(unsigned char * buff, int len)
{
    buff -= len;

    ofstream bufile ("buff.txt");

    if (bufile.is_open())
    {
        while (len--)
        {
            bufile << "buff: " << *buff << "len: " << len;
        }
        bufile.close();
    }
}

【问题讨论】:

    标签: c++ linux sockets memory


    【解决方案1】:

    假设您在 m_MsgBuf 中初始化了 m_MsgSize 个字节,并且您希望在一个循环块中逐块发送它们,其大小为

    std::min(m_MsgSize-sendBytes+2, 1000)
    

    因为“+2”取决于 m_MsgSize 的初始值,您可以在第一个 m_MsgSize 字节之后发送 1 或 2 个额外字节,这些额外字节可以是只是未初始化或什至超出 m_MsgBuf 且行为未定义

    请注意,如果你这样做了

      int32_t sendBytes = 0
    
      while (sendBytes != m_MsgSize) { /* != rather than < */
         int32_t b = send(socket, (char*)m_MsgBuf+sendBytes, std::min(m_MsgSize-sendBytes+2, 1000), ...);
    
         ... error management
         sendBytes += b;
      }
    

    由于 +2 引入的错误,您可以使 sendBytes 大于 m_MsgSize 并可能产生不良影响。

    只需删除那个“+2”


    [编辑问题/回答后编辑]

    如果 m_MsgSize 不计算额外的两个字节以发送下一个字节的长度,则您的 +2 不是问题

    valgrind 表示很少有错误,它们涉及缓冲区内的字节,而不是最后一个或 2 个字节,对我来说,这意味着您复制了 m_MsgBuf 几个未初始化的字节。有时 valgrind 在值未初始化时不会立即发出信号,例如,您可以:

    int i; // not initialized
    
    f(i); // nothing signaled by valgrind even i not initialized
    
    
    ...
    
    void f(int i)
    {
       int j = i + 1; // here valgrind signal a non initialized value
    

    我认为你就是这种情况。

    请注意,这可能不是错误,假设您这样做:

    // set some elements
    char s[20];
    ...    
    strcpy(s, "aze");
    ...
    memcpy(m_MsgBuf + 2, /* save 2 bytes for the length */
           s, sizeof(s));
    m_MsgSize = sizeof(s);
    ... may be some oher element added from m_MsgSize+2 and updating m_MsgSize
    NetworkMessage::WriteToSocket(socket);
    

    s 中仅初始化了 4 个字节,但要发送一个恒定大小,您发送(至少)20 个字节 => 缓冲区中的 16 个字节对应于未初始化的值 => valgrind 会向它们发出信号

    要知道您是否处于“非错误”情况,或者 valgrind 是否表示真正的问题,您必须分析您放入缓冲区中的所有值的设置,但再次警告 valgrind 可能需要一些时间来表示使用非初始化值,该值可能在到达您发送的 hte 缓冲区之前被多个中间位置使用过


    [从您的评论中编辑给出一段代码]

    初始化值可能来自NetworkMessage::AddXXX方法的调用者,例如NetworkMessage::AddByte在参数中接收一个未初始化的字节。

    事实上你有那个类有专门的方法在缓冲区中添加数据是一个机会,你可以用额外的代码修改它们的定义,人为地使用缓冲区中的字节来允许 valgrind 检测一个未初始化的值。您可以将受#ifdef ... #endif 保护的附加代码轻松激活/停用它

    比如(使用m_MsgBuf的类型来输入checkval的参数buff,我用的是“char *”,因为我没有知道 m_MsgBuf 的类型):

    #ifdef CHECKVAL
    extern int checkval(char * buff, int len);
    #endif
    
    void NetworkMessage::AddByte(unsigned char value)
    {
        if(!canAdd(1))
            return;
        m_MsgBuf[m_ReadPos++] = value;
        m_MsgSize++;
    #ifdef CHECKVAL
        checkval(m_MsgBuf+m_ReadPos, 1);
    #endif
    }
    
    void NetworkMessage::AddU16(uint16_t value)
    {
        if(!canAdd(2))
            return;
        m_MsgBuf[m_ReadPos++] = (unsigned char)(value);
        m_MsgBuf[m_ReadPos++] = (unsigned char)(value >> 8);
        m_MsgSize += 2;
    #ifdef CHECKVAL
        checkval(m_MsgBuf+m_ReadPos, 2);
    #endif
    }
    
    void NetworkMessage::AddU32(uint32_t value)
    {
        if(!canAdd(4))
            return;
        m_MsgBuf[m_ReadPos++] = (unsigned char)(value);
        m_MsgBuf[m_ReadPos++] = (unsigned char)(value >>  8);
        m_MsgBuf[m_ReadPos++] = (unsigned char)(value >> 16);
        m_MsgBuf[m_ReadPos++] = (unsigned char)(value >> 24);
        m_MsgSize += 4;
    #ifdef CHECKVAL
        checkval(m_MsgBuf+m_ReadPos, 4);
    #endif
    }
    
    void NetworkMessage::AddString(const char* value)
    {
        uint32_t stringlen = (uint32_t) strlen(value);
        if(!canAdd(stringlen+2) || stringlen > 8192)
            return;
    
    #ifdef USING_VISUAL_2005
        strcpy_s((char*)m_MsgBuf + m_ReadPos, stringlen, value); //VISUAL
    #else
        AddU16((uint16_t)stringlen);
        strcpy((char*)m_MsgBuf + m_ReadPos, value);
    #endif //USING_VISUAL_2005
        m_ReadPos += stringlen;
        m_MsgSize += stringlen;
    #ifdef CHECKVAL
        checkval(m_MsgBuf+m_ReadPos, stringlen);
    #endif
    }
    

    checkval 必须访问每个字节,例如:

    #ifdef CHECKVAL
    int checkval(char * buff, int len)
    {
       int r = 0;
    
       buff -= len;
       while (len--)
         r += *buff++;
       return r;
    }
    #endif
    

    并将 checkval 放在其他文件中,而不是定义 *NetworkMessage:Addxxx” 以确保编译器无法检测到它是无用的计算缓冲区中的字节总和或无用调用 checkval 因为从不使用返回值,并且 checkval 没有副作用。

    如果对字节求和不足以强制 valgrind 检查字节是否已初始化,请更改定义,例如将字节保存在文件中等

    当然是通过编译器选项编译定义CHECKVAL,或者只是添加临时的

    #define CHECKVAL
    

    checkval的定义之前和声明之前

    valgrind 将在 checkval 中检测到未初始化的字节时,您将能够通过查看同样由 生成的调用堆栈知道值来自哪里valgrind

    【讨论】:

    • 非常感谢您感兴趣!我照你说的做了,但现在我明白了
    • @Donniu 我根据您在问题中添加的信息和您的新答案编辑了我的答案,问题不是+2
    • 你很友好!非常感谢您的帮助!
    • @Donniu 你找到你的问题了吗?
    • @Donniu 你的链接wklejto.pl/828332 指向一个空白页面。无论如何,再次您是否在所有地方都激活了定义 CHECKVAL 的添加部分?在调试器中执行你的代码,在 checkval 中放置一个断点以确保它被调用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-31
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 2011-11-21
    • 1970-01-01
    相关资源
    最近更新 更多