IPC: Move the legacy key to the QNativeIpcKey

This is needed to support passing it to other processes so they can
enable legacy, compatibility mode. Right now, there's no such code, but
I am 90% certain we'll need it soon in 6.6.x, if not for compatibility
changes in the future.

There's a bug in passing a QNativeIpcKey to another process that causes
QSharedMemory to use the wrong QSystemSemaphore for control (a feature
that should never have existed in the first place, but we're 15 years
too late on that). I have not yet investigated a fix for this, but it
will likely involve knowing the original legacy key.

Pick-to: 6.6.0
Change-Id: Idd5e1bb52be047d7b4fffffd1750b547013cb336
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
(cherry picked from commit 9edb835904a7fa856e482464a7258019d5766333)
This commit is contained in:
Thiago Macieira 2023-03-28 15:19:14 -07:00 committed by Volker Hilsheimer
parent cc86ff5b3d
commit 271d38b0ef
7 changed files with 173 additions and 27 deletions

View File

@ -140,7 +140,6 @@ QSharedMemory::QSharedMemory(const QNativeIpcKey &key, QObject *parent)
QSharedMemory::QSharedMemory(const QString &key, QObject *parent) QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
: QSharedMemory(legacyNativeKey(key), parent) : QSharedMemory(legacyNativeKey(key), parent)
{ {
d_func()->legacyKey = key;
} }
#endif #endif
@ -185,9 +184,7 @@ QSharedMemory::~QSharedMemory()
*/ */
void QSharedMemory::setKey(const QString &key) void QSharedMemory::setKey(const QString &key)
{ {
Q_D(QSharedMemory);
setNativeKey(legacyNativeKey(key)); setNativeKey(legacyNativeKey(key));
d->legacyKey = key;
} }
#endif #endif
@ -245,7 +242,6 @@ void QSharedMemory::setNativeKey(const QNativeIpcKey &key)
if (isAttached()) if (isAttached())
detach(); detach();
d->cleanHandle(); d->cleanHandle();
d->legacyKey = QString();
if (key.type() == d->nativeKey.type()) { if (key.type() == d->nativeKey.type()) {
// we can reuse the backend // we can reuse the backend
d->nativeKey = key; d->nativeKey = key;
@ -314,7 +310,7 @@ bool QSharedMemoryPrivate::initKey(SemaphoreAccessMode mode)
QString QSharedMemory::key() const QString QSharedMemory::key() const
{ {
Q_D(const QSharedMemory); Q_D(const QSharedMemory);
return d->legacyKey; return QNativeIpcKeyPrivate::legacyKey(d->nativeKey);
} }
#endif #endif

View File

@ -205,8 +205,6 @@ public:
} }
QNativeIpcKey semaphoreNativeKey() const; QNativeIpcKey semaphoreNativeKey() const;
#endif // QT_CONFIG(systemsemaphore) #endif // QT_CONFIG(systemsemaphore)
QString legacyKey; // deprecated
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -84,7 +84,6 @@ inline void QSystemSemaphorePrivate::destructBackend()
QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessMode mode) QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessMode mode)
: QSystemSemaphore(legacyNativeKey(key), initialValue, mode) : QSystemSemaphore(legacyNativeKey(key), initialValue, mode)
{ {
d->legacyKey = key;
} }
#endif #endif
@ -210,8 +209,6 @@ void QSystemSemaphore::setNativeKey(const QNativeIpcKey &key, int initialValue,
} }
d->initialValue = initialValue; d->initialValue = initialValue;
d->handle(mode); d->handle(mode);
d->legacyKey.clear();
} }
/*! /*!
@ -244,7 +241,6 @@ QNativeIpcKey QSystemSemaphore::nativeIpcKey() const
void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode mode) void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode mode)
{ {
setNativeKey(legacyNativeKey(key), initialValue, mode); setNativeKey(legacyNativeKey(key), initialValue, mode);
d->legacyKey = key;
} }
/*! /*!
@ -256,7 +252,7 @@ void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode m
*/ */
QString QSystemSemaphore::key() const QString QSystemSemaphore::key() const
{ {
return d->legacyKey; return QNativeIpcKeyPrivate::legacyKey(d->nativeKey);
} }
#endif #endif

View File

@ -142,8 +142,6 @@ public:
{ {
return visit([&](auto p) { return p->modifySemaphore(this, count); }); return visit([&](auto p) { return p->modifySemaphore(this, count); });
} }
QString legacyKey; // deprecated
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -8,6 +8,7 @@
#include <qstandardpaths.h> #include <qstandardpaths.h>
#include <qstringconverter.h> #include <qstringconverter.h>
#include <qurl.h> #include <qurl.h>
#include <qurlquery.h>
#if defined(Q_OS_DARWIN) #if defined(Q_OS_DARWIN)
# include "private/qcore_mac_p.h" # include "private/qcore_mac_p.h"
@ -91,10 +92,11 @@ static QNativeIpcKey::Type stringToType(QStringView typeString)
Legacy: this exists for compatibility with QSharedMemory and Legacy: this exists for compatibility with QSharedMemory and
QSystemSemaphore between 4.4 and 6.6. QSystemSemaphore between 4.4 and 6.6.
Generate a string from the key which can be any unicode string into Returns a QNativeIpcKey that contains a platform-safe key using rules
the subset that the win/unix kernel allows. similar to QtIpcCommon::platformSafeKey() below, but using an algorithm
that is compatible with Qt 4.4 to 6.6. Additionally, the returned
On Unix this will be a file name QNativeIpcKey will record the input \a key so it can be included in the
string form if necessary to pass to other processes.
*/ */
QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType, QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType,
QNativeIpcKey::Type type) QNativeIpcKey::Type type)
@ -112,13 +114,14 @@ QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon
// to be in the form <application group identifier>/<custom identifier>. // to be in the form <application group identifier>/<custom identifier>.
// Since we don't know which application group identifier the user wants // Since we don't know which application group identifier the user wants
// to apply, we instead document that requirement, and use the key directly. // to apply, we instead document that requirement, and use the key directly.
k.setNativeKey(key); QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, key, key);
} else { } else {
// The shared memory name limit on Apple platforms is very low (30 characters), // The shared memory name limit on Apple platforms is very low (30 characters),
// so we can't use the logic below of combining the prefix, key, and a hash, // so we can't use the logic below of combining the prefix, key, and a hash,
// to ensure a unique and valid name. Instead we use the first part of the // to ensure a unique and valid name. Instead we use the first part of the
// hash, which should still long enough to avoid collisions in practice. // hash, which should still long enough to avoid collisions in practice.
k.setNativeKey(u'/' + QLatin1StringView(hex).left(SHM_NAME_MAX - 1)); QString native = u'/' + QLatin1StringView(hex).left(SHM_NAME_MAX - 1);
QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, native, key);
} }
return k; return k;
#endif #endif
@ -145,20 +148,30 @@ QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon
switch (type) { switch (type) {
case QNativeIpcKey::Type::Windows: case QNativeIpcKey::Type::Windows:
if (isIpcSupported(ipcType, QNativeIpcKey::Type::Windows)) if (isIpcSupported(ipcType, QNativeIpcKey::Type::Windows))
k.setNativeKey(result); QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
return k; return k;
case QNativeIpcKey::Type::PosixRealtime: case QNativeIpcKey::Type::PosixRealtime:
result.prepend(u'/');
if (isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime)) if (isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime))
k.setNativeKey(result.prepend(u'/')); QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
return k; return k;
case QNativeIpcKey::Type::SystemV: case QNativeIpcKey::Type::SystemV:
break; break;
} }
if (isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) if (isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) {
k.setNativeKey(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + u'/' + result); result = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + u'/' + result;
QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
}
return k; return k;
} }
/*!
\internal
Returns a QNativeIpcKey of type \a type, suitable for QSystemSemaphore or
QSharedMemory depending on \a ipcType. The returned native key is generated
from the Unicode input \a key and is safe for use on for the key type in
question in the current OS.
*/
QNativeIpcKey QtIpcCommon::platformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType, QNativeIpcKey QtIpcCommon::platformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType,
QNativeIpcKey::Type type) QNativeIpcKey::Type type)
{ {
@ -478,6 +491,7 @@ void QNativeIpcKey::setType_internal(Type type)
*/ */
void QNativeIpcKey::setNativeKey_internal(const QString &) void QNativeIpcKey::setNativeKey_internal(const QString &)
{ {
d->legacyKey_.clear();
} }
/*! /*!
@ -493,6 +507,8 @@ void QNativeIpcKey::setNativeKey_internal(const QString &)
*/ */
size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept
{ {
// by *choice*, we're not including d->legacyKey_ in the hash -- it's
// already partially encoded in the key
return qHashMulti(seed, ipcKey.key, ipcKey.type()); return qHashMulti(seed, ipcKey.key, ipcKey.type());
} }
@ -504,8 +520,7 @@ size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept
*/ */
int QNativeIpcKey::compare_internal(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept int QNativeIpcKey::compare_internal(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
{ {
Q_UNUSED(lhs); Q_UNUSED(rhs); return (QNativeIpcKeyPrivate::legacyKey(lhs) == QNativeIpcKeyPrivate::legacyKey(rhs)) ? 0 : 1;
return 0;
} }
/*! /*!
@ -534,6 +549,12 @@ QString QNativeIpcKey::toString() const
QUrl u; QUrl u;
u.setScheme(prefix); u.setScheme(prefix);
u.setPath(copy, QUrl::TolerantMode); u.setPath(copy, QUrl::TolerantMode);
if (isSlowPath()) {
QUrlQuery q;
if (!d->legacyKey_.isEmpty())
q.addQueryItem(u"legacyKey"_s, QString(d->legacyKey_).replace(u'%', "%25"_L1));
u.setQuery(q);
}
return u.toString(QUrl::DecodeReserved); return u.toString(QUrl::DecodeReserved);
} }
@ -553,7 +574,7 @@ QNativeIpcKey QNativeIpcKey::fromString(const QString &text)
Type invalidType = {}; Type invalidType = {};
Type type = stringToType(u.scheme()); Type type = stringToType(u.scheme());
if (type == invalidType || !u.isValid() || !u.userInfo().isEmpty() || !u.host().isEmpty() if (type == invalidType || !u.isValid() || !u.userInfo().isEmpty() || !u.host().isEmpty()
|| u.port() != -1 || u.hasQuery()) || u.port() != -1)
return QNativeIpcKey(invalidType); return QNativeIpcKey(invalidType);
QNativeIpcKey result(QString(), type); QNativeIpcKey result(QString(), type);
@ -563,6 +584,18 @@ QNativeIpcKey QNativeIpcKey::fromString(const QString &text)
// decode the payload // decode the payload
result.setNativeKey(u.path()); result.setNativeKey(u.path());
if (u.hasQuery()) {
const QList items = QUrlQuery(u).queryItems();
for (const auto &item : items) {
if (item.first == u"legacyKey"_s) {
QString legacyKey = QUrl::fromPercentEncoding(item.second.toUtf8());
QNativeIpcKeyPrivate::setLegacyKey(result, std::move(legacyKey));
} else {
// unknown query item
return QNativeIpcKey(invalidType);
}
}
}
return result; return result;
} }

View File

@ -31,6 +31,32 @@ QT_BEGIN_NAMESPACE
class QNativeIpcKeyPrivate class QNativeIpcKeyPrivate
{ {
public: public:
QString legacyKey_;
static QString legacyKey(const QNativeIpcKey &key)
{
if (key.isSlowPath())
return key.d->legacyKey_;
return QString();
}
static void setLegacyKey(QNativeIpcKey &key, const QString &legacyKey)
{
QNativeIpcKeyPrivate::makeExtended(key)->legacyKey_ = legacyKey;
}
static void setNativeAndLegacyKeys(QNativeIpcKey &key, const QString &nativeKey,
const QString &legacyKey)
{
key.setNativeKey(nativeKey);
setLegacyKey(key, legacyKey);
}
private:
static QNativeIpcKeyPrivate *makeExtended(QNativeIpcKey &key)
{
if (!key.isSlowPath())
key.d = new QNativeIpcKeyPrivate;
return key.d;
}
}; };
namespace QtIpcCommon { namespace QtIpcCommon {

View File

@ -6,6 +6,19 @@
#include "../ipctestcommon.h" #include "../ipctestcommon.h"
#if QT_CONFIG(sharedmemory)
# include <qsharedmemory.h>
#endif
#if QT_CONFIG(systemsemaphore)
# include <qsystemsemaphore.h>
#endif
#if QT_CONFIG(sharedmemory)
static const auto makeLegacyKey = QSharedMemory::legacyNativeKey;
#else
static const auto makeLegacyKey = QSystemSemaphore::legacyNativeKey;
#endif
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
class tst_QNativeIpcKey : public QObject class tst_QNativeIpcKey : public QObject
@ -22,6 +35,8 @@ private slots:
void toString(); void toString();
void fromString_data(); void fromString_data();
void fromString(); void fromString();
void legacyKeys_data();
void legacyKeys();
}; };
void tst_QNativeIpcKey::defaultTypes() void tst_QNativeIpcKey::defaultTypes()
@ -181,6 +196,19 @@ void tst_QNativeIpcKey::equality()
key2.setType(QNativeIpcKey::DefaultTypeForOs); key2.setType(QNativeIpcKey::DefaultTypeForOs);
QCOMPARE(key1, key2); QCOMPARE(key1, key2);
QVERIFY(!(key1 != key2)); QVERIFY(!(key1 != key2));
key1 = makeLegacyKey("key1", QNativeIpcKey::DefaultTypeForOs);
QCOMPARE_NE(key1, key2);
QVERIFY(!(key1 == key2));
key2 = key1;
QCOMPARE(key1, key2);
QVERIFY(!(key1 != key2));
// just setting the native key won't make them equal again!
key2.setNativeKey(key1.nativeKey());
QCOMPARE_NE(key1, key2);
QVERIFY(!(key1 == key2));
} }
void tst_QNativeIpcKey::hash() void tst_QNativeIpcKey::hash()
@ -215,6 +243,12 @@ void tst_QNativeIpcKey::swap()
QCOMPARE(key1.type(), QNativeIpcKey::Type::PosixRealtime); QCOMPARE(key1.type(), QNativeIpcKey::Type::PosixRealtime);
QCOMPARE(key2.nativeKey(), "key2"); QCOMPARE(key2.nativeKey(), "key2");
QCOMPARE(key2.type(), QNativeIpcKey::Type::Windows); QCOMPARE(key2.type(), QNativeIpcKey::Type::Windows);
key1 = makeLegacyKey("key1", QNativeIpcKey::DefaultTypeForOs);
QCOMPARE(key1.type(), QNativeIpcKey::DefaultTypeForOs);
key1.swap(key2);
QCOMPARE(key1.type(), QNativeIpcKey::Type::Windows);
QCOMPARE(key2.type(), QNativeIpcKey::DefaultTypeForOs);
} }
void tst_QNativeIpcKey::toString_data() void tst_QNativeIpcKey::toString_data()
@ -323,5 +357,70 @@ void tst_QNativeIpcKey::fromString()
QCOMPARE(QNativeIpcKey::fromString(string), key); QCOMPARE(QNativeIpcKey::fromString(string), key);
} }
void tst_QNativeIpcKey::legacyKeys_data()
{
QTest::addColumn<QNativeIpcKey::Type>("type");
QTest::addColumn<QString>("legacyKey");
auto addRows = [](QNativeIpcKey::Type type) {
const char *label = "<unknown-type>";
switch (type) {
case QNativeIpcKey::Type::SystemV:
label = "systemv";
break;
case QNativeIpcKey::Type::PosixRealtime:
label = "posix";
break;
case QNativeIpcKey::Type::Windows:
label = "windows";
break;
}
auto add = [=](const char *name, const QString &legacyKey) {
QTest::addRow("%s-%s", label, name) << type << legacyKey;
};
add("empty", {});
add("text", "foobar"_L1);
add("pathlike", "/sometext"_L1);
add("objectlike", "Global\\sometext"_L1);
add("colon-slash", ":/"_L1);
add("slash-colon", "/:"_L1);
add("percent", "%"_L1);
add("question-hash", "?#"_L1);
add("hash-question", "#?"_L1);
add("double-slash", "//"_L1);
add("triple-slash", "///"_L1);
add("non-ascii", "\xe9"_L1);
add("non-utf8", "\xa0\xff"_L1);
add("non-latin1", u":\u0100.\u2000.\U00010000"_s);
};
addRows(QNativeIpcKey::DefaultTypeForOs);
if (auto type = QNativeIpcKey::legacyDefaultTypeForOs();
type != QNativeIpcKey::DefaultTypeForOs)
addRows(type);
}
void tst_QNativeIpcKey::legacyKeys()
{
QFETCH(QNativeIpcKey::Type, type);
QFETCH(QString, legacyKey);
QNativeIpcKey key = makeLegacyKey(legacyKey, type);
QCOMPARE(key.type(), type);
QString string = key.toString();
QNativeIpcKey key2 = QNativeIpcKey::fromString(string);
QCOMPARE(key2, key);
if (!legacyKey.isEmpty()) {
// confirm it shows up in the encoded form
Q_ASSERT(!legacyKey.contains(u'&')); // needs extra encoding
QUrl u;
u.setQuery("legacyKey="_L1 + legacyKey, QUrl::DecodedMode);
QString encodedLegacyKey = u.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority
| QUrl::DecodeReserved);
QVERIFY2(string.contains(encodedLegacyKey), qPrintable(string));
}
}
QTEST_MAIN(tst_QNativeIpcKey) QTEST_MAIN(tst_QNativeIpcKey)
#include "tst_qnativeipckey.moc" #include "tst_qnativeipckey.moc"