简体   繁体   English

C++/Qt memcpy 与 QSharedMemory 崩溃

[英]C++/Qt memcpy crash with QSharedMemory

I have a simple function which sends strings (uri links or filepaths) to an already running instance of the application using Qt's (5.5.1) QSharedMemory class.我有一个简单的函数,它使用 Qt (5.5.1) QSharedMemory 类将字符串(uri 链接或文件路径)发送到已运行的应用程序实例。

It seems to work correctly for most of the times, but i caught a crash log from a user, where it crashed on a memcpy.它似乎在大多数情况下都能正常工作,但我从用户那里捕获了崩溃日志,它在 memcpy 上崩溃了。 The function looks the following:该函数如下所示:

void WindowsApp::SendData( char* uri )
{
    int size = 1024;
    if (!m_SharedMemory.create(size)) {
        qDebug() << "Unable to create shared memory segment." << m_SharedMemory.error();
        return;
    }
    m_SharedMemory.lock();
    char *to = (char*)m_SharedMemory.data();
    const char *from = uri;
    memcpy(to, from, qMin(m_SharedMemory.size(), size));
    m_SharedMemory.unlock();
    QThread::sleep(10);
}

m_SharedMemory is a QSharedMemory type static member of the class. m_SharedMemory 是类的 QSharedMemory 类型静态成员。

From the log, i have seen that the string which i try to send is a simple filepath with no special characters, and not too long, only 150 chars.从日志中,我看到我尝试发送的字符串是一个简单的文件路径,没有特殊字符,而且不太长,只有 150 个字符。

What can be wrong , but so that i couldn't reproduce it with similar parameters?有什么问题,但我无法用类似的参数重现它?

The code has undefined behavior since you're reading past the end of the source string: you always read 1024 bytes even if the source string is eg 5 bytes long.由于您正在阅读源字符串的末尾,因此代码具有未定义的行为:即使源字符串的长度为 5 个字节,您也始终读取 1024 个字节。 UB isn't a guarantee of a crash as you've noted.正如您所指出的,UB 不是崩溃的保证。 Usually it will crash during an important demo.通常它会在重要的演示中崩溃。 You're also not ensuring that the string will be zero-terminated if it's too long to fit in the memory segment, thus the receiver may crash if it attempts to treat the string as if it was zero-terminated.如果字符串太长而无法放入内存段,您也不能确保该字符串将以零结尾,因此如果接收器尝试将字符串视为以零结尾,则它可能会崩溃。

These issues are likely due to lack of design.这些问题很可能是由于缺乏设计。 The contents of the memory segment imply a contract between the sender and the receiver.内存段的内容意味着发送者和接收者之间的契约。 Both must agree on something.双方必须就某事达成一致。 Let's define the contract:让我们定义合约:

  1. The shared memory segment's contents are a null-terminated C string.共享内存段的内容是以空字符结尾的 C 字符串。

    This is a so-called invariant: it is always true, no matter what.这就是所谓的不变量:无论如何,它总是正确的。 This enables the reader to use C-string APIs safely without having to check first if there's a null termination.这使读者能够安全地使用 C 字符串 API,而不必先检查是否存在空终止。

  2. A uri that's too long is replaced by an empty string.太长的 uri 将替换为空字符串。

    This is a post-condition to the writer: it implies that the writing will either place a complete URI in the memory, or an empty string.这是写入器的后置条件:它意味着写入将在内存中放置一个完整的 URI,或一个空字符串。

Here's how you could fix it:以下是您可以修复它的方法:

bool WindowsApp::WriteShared(const char * src, int length) {
   if (m_SharedMemory.lock()) {
      auto const dst = static_cast<char*>(m_SharedMemory.data());
      Q_ASSERT(dst);
      memcpy(dst, src, length);
      m_SharedMemory.unlock();
      return true;
   }
   return false;
}

bool WindowsApp::SendData(const char* uri)
{
   Q_ASSERT(uri);
   if (!m_SharedMemory.create(1024)) {
      qWarning() << "Unable to create shared memory segment." << m_SharedMemory.error();
      return false;
   }
   int const uriLength = strlen(uri) + 1;
   if (uriLength > m_SharedMemory.size()) {
      qWarning() << "The uri is too long.";
      if (! WriteShared("", 1))
         qWarning() << "Can't clear the memory.";
      return false;
   }
   if (! WriteShared(uri, uriLength)) {
      qWarning() << "Can't lock the shared memory segment.";
      return false;
   }
   QThread::sleep(10);
   return true;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM