0

我下面的代码是一个简单的错误记录系统,其行为方式类似于 printf。

我的所有代码都在 gtest 环境中运行良好,但现在当我退出程序中的一个确定性点(我的一个测试)时,它堆栈崩溃了。在我写这篇文章之前,我的代码一直运行良好,这是我第一次尝试 cstdarg,所以它是最可疑的。

MyMutex error_lock;

#define MAX_ERR_MSG_SIZE 128
#define MAX_ERRORS 3
class ErrorQueue
{
   std::queue<char*> errors;
   std::list<char*> old_errors;
public:
   void push(char * msg)
   {
      if (errors.size() >= MAX_ERRORS)
      {
         pop();
      }
      errors.push(msg);
   }

   void pop()
   {
      if (old_errors.size() >= MAX_ERRORS)
      {
         delete [] old_errors.front();
         old_errors.pop_front();
      }
      old_errors.push_back(errors.front());
      errors.pop();
   }

   char * front()
   {
      return errors.front();
   }

   size_t size()
   {
      return errors.size();
   }

   ~ErrorQueue()
   {
      while(!errors.empty())
      {
         delete [] errors.front();
         errors.pop();
      }

      while (!old_errors.empty())
      {
         delete [] old_errors.front();
         old_errors.pop_front();
      }

   }

};

static ErrorQueue errors;


void WriteCallError(const char * error_message, ...)
{
   char err_buffer[MAX_ERR_MSG_SIZE];

   va_list args;
   va_start(args,error_message);
   std::vsnprintf(err_buffer,MAX_ERR_MSG_SIZE,error_message,args);
   va_end(args);

   char * err_string = new char[MAX_ERR_MSG_SIZE];
   memcpy(err_string,err_buffer,MAX_ERR_MSG_SIZE);
   {
      error_lock.Lock();
      errors.push(err_string);
      error_lock.Unlock();
   }
}

我在代码的其他地方多次调用 WriteCallError ,经过一定次数后它会呕吐并告诉我我的堆栈已粉碎。

我的错在哪里?cstdarg 和 gtest 之间有什么奇怪的交互吗?这里有足够的信息吗?

编辑:使用一个简单的 main 我尝试将其隔离:

int main (int argc, char ** argv)
{
   int i = 0;
   while (1)
   {
      WriteCallError("Breaks on %d",i++);
   }

}

这不会导致堆栈粉碎。

4

2 回答 2

2

我认为您的问题是,如果您正在写入的数据超出您指定的限制,则 vsnprintf 不会将终止 nul 字符写入数组。当您稍后访问此 C 字符串时,由于没有终止符,它将读取超出有效数据的末尾并进入未知内存。

您没有在测试中发现这一点,因为您没有超过 128 个字符的限制。

快速修复是确保在使用 vsnprintf 后正确终止数组:

void WriteCallError(const char * error_message, ...)
{
   char err_buffer[MAX_ERR_MSG_SIZE];

   va_list args;
   va_start(args,error_message);
   std::vsnprintf(err_buffer,MAX_ERR_MSG_SIZE,error_message,args);
   va_end(args);

   // Fix here
   err_buffer[MAX_ERR_MSG_SIZE - 1] = 0;

   char * err_string = new char[MAX_ERR_MSG_SIZE];
   memcpy(err_string,err_buffer,MAX_ERR_MSG_SIZE);
   {
      error_lock.Lock();
      errors.push(err_string);
      error_lock.Unlock();
   }
}
于 2013-02-20T13:03:26.137 回答
0

回答这样我就可以关闭问题,因为我解决了这个错误:

它发生在与此类似的代码中:

TEST(MyTest,Case1)
{
   MyStruct1 object1;
   memset(&object1,0,sizeof(object1));

   MyStruct2 object2[1];
   memset(&object2[0],0,sizeof(object1));

   object1.object2_ptr = object2;


   // DO SOME TESTING

}

由于 object1 和 object2 是堆栈变量并且 object1 大于 object2 我在 memset 时将内存设置得太远。当我离开该函数的堆栈部分时,堆栈指针大喊发生了一些疯狂的事情,并告诉我我正在破坏堆栈。

简单修复:

TEST(MyTest,Case1)
{
   MyStruct1 object1;
   memset(&object1,0,sizeof(MyStruct1));

   MyStruct2 object2[1];
   memset(&object2[0],0,sizeof(MyStruct2));

   object1.object2_ptr = object2;


   // DO SOME TESTING

}
于 2013-02-20T16:29:35.310 回答