Сбой C ++ / Qt memcpy с QSharedMemory

У меня есть простая функция, которая отправляет строки (ссылки URI или пути к файлам) в уже запущенный экземпляр приложения, используя класс Qt (5.5.1) QSharedMemory.

Кажется, он работает правильно в большинстве случаев, но я получил журнал сбоя от пользователя, где он вылетел на memcpy. Функция выглядит следующим образом:

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 — статический член класса QSharedMemory.

Из журнала я увидел, что строка, которую я пытаюсь отправить, представляет собой простой путь к файлу без специальных символов и не слишком длинный, всего 150 символов.

Что может быть не так, но чтобы я не смог воспроизвести его с похожими параметрами?

1

Решение

Код имеет неопределенное поведение, так как вы читаете после конца исходной строки: вы всегда читаете 1024 байта, даже если исходной строкой является, например, 5 байт UB не является гарантией краха, как вы заметили. Обычно он вылетает во время важной демонстрации. Вы также не гарантируете, что строка будет оканчиваться нулем, если она слишком длинна, чтобы поместиться в сегмент памяти, поэтому получатель может аварийно завершить работу, если попытается обработать строку так, как если бы она была оканчивалась нулем.

Эти проблемы, вероятно, из-за отсутствия дизайна. Содержимое сегмента памяти подразумевает договор между отправителем и получателем. Оба должны договориться о чем-то. Определим договор:

  1. Содержимое сегмента общей памяти представляет собой строку C с нулевым символом в конце.

    Это так называемый инвариант: он всегда верен, несмотря ни на что. Это позволяет читателю безопасно использовать API-интерфейсы C-string без необходимости сначала проверять наличие нулевого завершения.

  2. Слишком длинный URI заменяется пустой строкой.

    Это постусловие для автора: оно подразумевает, что запись либо поместит полный URI в память, либо пустую строку.

Вот как вы можете это исправить:

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;
}
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]