在函数结束后仅释放 Segfault 与 vsnprintf 一起消失



我正在破解teeworlds的服务器代码,但是我正在尝试一个奇怪的错误。我已经遇到了非常相似的事情,但我发现了一个不干净的解决方法。

正如我在帖子标题中所说,我有一个仅在发布版本中出现的段错误。我使用 printfs 定位了该行,因为 valgrind 和 gdb 都没有显示调试版本的错误。

这是完整的代码,段错误发生在调用此函数之后:

  void CServer::SetClientName(int ClientID, const char *pName)
  {
     if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY)
        return;
     if(!pName)
        return;
     char * pDispName;
     char aCleanName[MAX_NAME_LENGTH];
     Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", "TEST");
     // clean name
     int lastpos = 0;
     {
        int i = 0;
        const char *psrc = pName; 
        char *pdst = aCleanName;
        for(; *psrc != '' && *psrc<=' '; ++psrc);
        while(*psrc != '' && i<sizeof(aCleanName)-1)
        {
           if(*psrc <= ' ')
           {
              *pdst = ' ';
           }
           else
           {
              *pdst = *psrc;
              lastpos = i+1;
           }
           ++psrc;
           ++pdst;
           ++i;
        }
        aCleanName[lastpos] = ''; //Rtrim
     }
     Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", "TEST2");
/////////////// These lines if not commented prevent the segfault
//     char aBuf[256];
//     str_format(aBuf, sizeof(aBuf), "CleanName : '%s, lastpos = %i'", aCleanName, lastpos);
     Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", aCleanName);
     // set real name
     str_copy(m_aClients[ClientID].m_aName, aCleanName, sizeof(aCleanName));
     Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", "TEST3");
     // DispName
     if(m_aClients[ClientID].m_aUserAcc[0] != '')
     {
        Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", "AUTHED");
        pDispName = m_aClients[ClientID].m_aName;
     }
     else
     {
        Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", "NOT AUTHED");
        str_percent_format(aCleanName, sizeof(aCleanName), g_Config.m_SvNotAuthedFormat, m_aClients[ClientID].m_aName);
        pDispName = aCleanName;
     }
     Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", "TEST4");
     if(TrySetClientDispName(ClientID, pDispName))
     {
        Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", "TRY FAILED");
        // auto rename
        int j = 0;
        for( ; j<MAX_NAME_LENGTH-3 && aCleanName[j]!='' ; ++j);
        Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", "TEST5");
        for( ; j<MAX_NAME_LENGTH-3; ++j)
           aCleanName[j] = ' ';
        Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", "TEST6");
        aCleanName[MAX_NAME_LENGTH-3] = '#';
        aCleanName[MAX_NAME_LENGTH-2] = 'a';
        aCleanName[MAX_NAME_LENGTH-1] = '';
        Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", "TEST7");
        for(int i = 0; i<MAX_CLIENTS; ++i) //avoid infinite loop
        {
           Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", "TEST9");
           if(TrySetClientDispName(ClientID, aCleanName) == 0)
              break;
           ++aCleanName[MAX_NAME_LENGTH-2];
        }
     }
     Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "DEBUG", "TEST10");
  }

打印 TEST10,但休耕调用的语句永远不会

注意:TrySetClientDispName(ClientID, aCleanName)不存储指向 aCleanName 的指针,

str_format实现如下:

  void str_format(char *buffer, int buffer_size, const char *format, ...)
  {
     va_list ap;
     va_start(ap, format);
     vsnprintf(buffer, buffer_size, format, ap);
     va_end(ap);
     buffer[buffer_size-1] = 0; /* assure null termination */
  }

我想,这是堆栈损坏,但是在哪里?那么为什么str_format可以防止错误?

编辑:也许str_percent_format(aCleanName, sizeof(aCleanName), g_Config.m_SvNotAuthedFormat, m_aClients[ClientID].m_aName)在 aCleanName 中写的比它所能容纳的更多

就是

这样。

此类错误中,如果缓冲区在与另一个函数一起使用时具有奇怪的行为,并且在函数结束后出现段错误,这是因为缓冲区溢出损坏了调用堆栈。

顺便说一句,这可以做一个很好的后门:D

相关内容

  • 没有找到相关文章

最新更新