Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtipccommon.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 Intel Corporation.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qtipccommon.h"
7
8#include <qcryptographichash.h>
9#include <qstandardpaths.h>
10#include <qstringconverter.h>
11#include <qurl.h>
12#include <qurlquery.h>
13
14#if defined(Q_OS_DARWIN)
15# include "private/qcore_mac_p.h"
16# if !defined(SHM_NAME_MAX)
17 // Based on PSEMNAMLEN in XNU's posix_sem.c, which would
18 // indicate the max length is 31, _excluding_ the zero
19 // terminator. But in practice (possibly due to an off-
20 // by-one bug in the kernel) the usable bytes are only 30.
21# define SHM_NAME_MAX 30
22# endif
23#elif defined(Q_OS_WINDOWS)
24# include "qt_windows.h"
25#endif
26
27#if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
28
29QT_BEGIN_NAMESPACE
30
31using namespace Qt::StringLiterals;
32
33static QStringView staticTypeToString(QNativeIpcKey::Type type)
34{
35 switch (type) {
36 case QNativeIpcKey::Type::SystemV:
37 return u"systemv";
38 case QNativeIpcKey::Type::PosixRealtime:
39 return u"posix";
40 case QNativeIpcKey::Type::Windows:
41 return u"windows";
42 }
43 return {};
44}
45
46static QString typeToString(QNativeIpcKey::Type type)
47{
48 QStringView typeString = staticTypeToString(type);
49 switch (type) {
50 case QNativeIpcKey::Type::SystemV:
51 case QNativeIpcKey::Type::PosixRealtime:
52 case QNativeIpcKey::Type::Windows:
53 return QString::fromRawData(typeString.constData(), typeString.size());
54 }
55
56 int value = int(type);
57 if (value >= 1 && value <= 0xff) {
58 // System V key with id different from 'Q'
59 typeString = staticTypeToString(QNativeIpcKey::Type::SystemV);
60 return typeString + QString::number(-value); // negative so it prepends a dash
61 }
62
63 return QString(); // invalid!
64}
65
66static QNativeIpcKey::Type stringToType(QStringView typeString)
67{
68 if (typeString == staticTypeToString(QNativeIpcKey::Type::PosixRealtime))
69 return QNativeIpcKey::Type::PosixRealtime;
70 if (typeString == staticTypeToString(QNativeIpcKey::Type::Windows))
71 return QNativeIpcKey::Type::Windows;
72
73 auto fromNumber = [](QStringView number, int low, int high) {
74 bool ok;
75 int n = -number.toInt(&ok, 10);
76 if (!ok || n < low || n > high)
77 return QNativeIpcKey::Type{};
78 return QNativeIpcKey::Type(n);
79 };
80
81 QStringView sysv = staticTypeToString(QNativeIpcKey::Type::SystemV);
82 if (typeString.startsWith(sysv)) {
83 if (typeString.size() == sysv.size())
84 return QNativeIpcKey::Type::SystemV;
85 return fromNumber(typeString.sliced(sysv.size()), 1, 0xff);
86 }
87
88 // invalid!
89 return QNativeIpcKey::Type{};
90}
91
92/*!
93 \internal
94
95 Legacy: this exists for compatibility with QSharedMemory and
96 QSystemSemaphore between 4.4 and 6.6.
97
98 Returns a QNativeIpcKey that contains a platform-safe key using rules
99 similar to QtIpcCommon::platformSafeKey() below, but using an algorithm
100 that is compatible with Qt 4.4 to 6.6. Additionally, the returned
101 QNativeIpcKey will record the input \a key so it can be included in the
102 string form if necessary to pass to other processes.
103*/
104QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType,
105 QNativeIpcKey::Type type)
106{
107 QNativeIpcKey k(type);
108 if (key.isEmpty())
109 return k;
110
111 QByteArray hex = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toHex();
112
113 if (type == QNativeIpcKey::Type::PosixRealtime) {
114#if defined(Q_OS_DARWIN)
115 if (qt_apple_isSandboxed()) {
116 // Sandboxed applications on Apple platforms require the shared memory name
117 // to be in the form <application group identifier>/<custom identifier>.
118 // Since we don't know which application group identifier the user wants
119 // to apply, we instead document that requirement, and use the key directly.
120 QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, key, key);
121 } else {
122 // The shared memory name limit on Apple platforms is very low (30 characters),
123 // so we can't use the logic below of combining the prefix, key, and a hash,
124 // to ensure a unique and valid name. Instead we use the first part of the
125 // hash, which should still long enough to avoid collisions in practice.
126 QString native = u'/' + QLatin1StringView(hex).left(SHM_NAME_MAX - 1);
127 QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, native, key);
128 }
129 return k;
130#endif
131 }
132
133 QString result;
134 result.reserve(1 + 18 + key.size() + 40);
135 switch (ipcType) {
136 case IpcType::SharedMemory:
137 result += "qipc_sharedmemory_"_L1;
138 break;
139 case IpcType::SystemSemaphore:
140 result += "qipc_systemsem_"_L1;
141 break;
142 }
143
144 for (QChar ch : key) {
145 if ((ch >= u'a' && ch <= u'z') ||
146 (ch >= u'A' && ch <= u'Z'))
147 result += ch;
148 }
149 result.append(QLatin1StringView(hex));
150
151 switch (type) {
152 case QNativeIpcKey::Type::Windows:
153 if (isIpcSupported(ipcType, QNativeIpcKey::Type::Windows))
154 QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
155 return k;
156 case QNativeIpcKey::Type::PosixRealtime:
157 result.prepend(u'/');
158 if (isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime))
159 QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
160 return k;
161 case QNativeIpcKey::Type::SystemV:
162 break;
163 }
164 if (isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) {
165 result = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + u'/' + result;
166 QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
167 }
168 return k;
169}
170
171/*!
172 \internal
173 Returns a QNativeIpcKey of type \a type, suitable for QSystemSemaphore or
174 QSharedMemory depending on \a ipcType. The returned native key is generated
175 from the Unicode input \a key and is safe for use on for the key type in
176 question in the current OS.
177*/
178QNativeIpcKey QtIpcCommon::platformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType,
179 QNativeIpcKey::Type type)
180{
181 QNativeIpcKey k(type);
182 if (key.isEmpty())
183 return k;
184
185 switch (type) {
186 case QNativeIpcKey::Type::PosixRealtime:
187 if (isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime)) {
188#ifdef SHM_NAME_MAX
189 // The shared memory name limit on Apple platforms is very low (30
190 // characters), so we have to cut it down to avoid ENAMETOOLONG. We
191 // hope that there won't be too many collisions...
192 k.setNativeKey(u'/' + QStringView(key).left(SHM_NAME_MAX - 1));
193#else
194#if defined(Q_OS_OHOS)
195 // HarmonyOS appears to have same namespace for POSIX shared memory
196 // and semaphores, so we need to add a suffix to avoid collisions
197 QStringView suffix;
198 switch (ipcType) {
199 case IpcType::SharedMemory:
200 suffix = u"_shm"; break;
201 case IpcType::SystemSemaphore:
202 suffix = u"_sem"; break;
203 }
204 k.setNativeKey(u'/' + key + suffix);
205#else
206 k.setNativeKey(u'/' + key);
207#endif
208#endif
209 }
210 return k;
211
212 case QNativeIpcKey::Type::Windows:
213 if (isIpcSupported(ipcType, QNativeIpcKey::Type::Windows)) {
214 QStringView prefix;
215 QStringView payload = key;
216 // see https://learn.microsoft.com/en-us/windows/win32/termserv/kernel-object-namespaces
217 for (QStringView candidate : { u"Local\\"_sv, u"Global\\"_sv }) {
218 if (!key.startsWith(candidate))
219 continue;
220 prefix = candidate;
221 payload = payload.sliced(prefix.size());
222 break;
223 }
224
225 QStringView mid;
226 switch (ipcType) {
227 case IpcType::SharedMemory: mid = u"shm_"; break;
228 case IpcType::SystemSemaphore: mid = u"sem_"; break;
229 }
230
231 QString result = prefix + mid + payload;
232#ifdef Q_OS_WINDOWS
233 result.truncate(MAX_PATH);
234#endif
235 k.setNativeKey(result);
236 }
237 return k;
238
239 case QNativeIpcKey::Type::SystemV:
240 break;
241 }
242
243 // System V
244 if (isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) {
245 if (key.startsWith(u'/')) {
246 k.setNativeKey(key);
247 } else {
248 QString baseDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
249 k.setNativeKey(baseDir + u'/' + key);
250 }
251 }
252 return k;
253}
254
255/*!
256 \class QNativeIpcKey
257 \inmodule QtCore
258 \since 6.6
259 \brief The QNativeIpcKey class holds a native key used by QSystemSemaphore and QSharedMemory.
260
261 \compares equality
262
263 The \l QSharedMemory and \l QSystemSemaphore classes identify their
264 resource using a system-wide identifier known as a "key". The low-level key
265 value as well as the key type are encapsulated in Qt using the \l
266 QNativeIpcKey class.
267
268 Those two classes also provide the means to create native keys from a
269 cross-platform identifier, using QSharedMemory::platformSafeKey() and
270 QSystemSemaphore::platformSafeKey(). Applications should never share the
271 input to those functions, as different versions of Qt may perform different
272 transformations, resulting in different native keys. Instead, the
273 application that created the IPC object should communicate the resulting
274 native key using the methods described below.
275
276 For details on the key types, platform-specific limitations, and
277 interoperability with older or non-Qt applications, see the \l{Native IPC
278 Keys} documentation. That includes important information for sandboxed
279 applications on Apple platforms, including all apps obtained via the Apple
280 App Store.
281
282 \section1 Communicating keys to other processes
283 \section2 Communicating keys to other Qt processes
284
285 If the other process supports QNativeIpcKey, the best way of communicating
286 is via the string representation obtained from toString() and parsing it
287 using fromString(). This representation can be stored on a file whose name
288 is well-known or passed on the command-line to a child process using
289 QProcess::setArguments().
290
291 If the other process does not support QNativeIpcKey, then the two processes
292 can exchange the nativeKey() but the older code is likely unable to adjust
293 its key type. The legacyDefaultTypeForOs() function returns the type that
294 legacy code used, which may not match the \l{DefaultTypeForOs} constant.
295 This is still true even if the old application is not using the same build
296 as the new one (for example, it is a Qt 5 application), provided the
297 options passed to the Qt configure script are the same.
298
299 \section2 Communicating keys to non-Qt processes
300
301 When communicating with non-Qt processes, the application must arrange to
302 obtain the key type the other process is using. This is important
303 particularly on Unix systems, where both \l PosixRealtime and \l SystemV
304 are common.
305
306 \section1 String representation of native keys
307
308 The format of the string representation of a QNativeIpcKey is meant to be
309 stable and therefore backwards and forwards compatible, provided the key
310 type is supported by the Qt version in question. That is to say, an older
311 Qt will fail to parse the string representation of a key type introduced
312 after it was released. However, successfully parsing a string
313 representation does not imply the Qt classes can successfully create an
314 object of that type; applications should verify support using
315 QSharedMemory::isKeyTypeSupported() and QSystemSemaphore::isKeyTypeSupported().
316
317 The format of the string representation is formed by two components,
318 separated by a colon (':'). The first component is the key type, described
319 in the table below. The second component is a type-specific payload, using
320 \l{QByteArray::fromPercentEncoding}{percent-encoding}. For all currently
321 supported key types, the decoded form is identical to the contents of the
322 nativeKey() field.
323
324 \table
325 \row \li Key type \li String representation
326 \row \li \l PosixRealtime \li \c "posix"
327 \row \li \l SystemV \li \c "systemv"
328 \row \li \l Windows \li \c "windows"
329 \row \li Non-standard SystemV \li \c "systemv-" followed by a decimal number
330 \endtable
331
332 This format resembles a URI and allows parsing using URI/URL-parsing
333 functions, such as \l QUrl. When parsed by such API, the key type will show
334 up as the \l{QUrl::scheme()}{scheme}, and the payload will be the
335 \l{QUrl::path()}{path}. Use of query or fragments is reserved.
336
337 \sa QSharedMemory, QSystemSemaphore
338*/
339
340/*!
341 \enum QNativeIpcKey::Type
342
343 This enum describes the backend type for the IPC object. For details on the
344 key types, see the \l{Native IPC Keys} documentation.
345
346 \value SystemV X/Open System Initiative (XSI) or System V (SVr4) API
347 \value PosixRealtime IEEE 1003.1b (POSIX.1b) API
348 \value Windows Win32 API
349
350 \sa setType(), type()
351*/
352
353/*!
354 \variable QNativeIpcKey::DefaultTypeForOs
355
356 This constant expression variable holds the default native IPC type for the
357 current OS. It will be Type::Windows for Windows systems and
358 Type::PosixRealtime elsewhere. Note that this constant is different from
359 what \l QSharedMemory and \l QSystemSemaphore defaulted to on the majority
360 of Unix systems prior to Qt 6.6; see legacyDefaultTypeForOs() for more
361 information.
362*/
363
364/*!
365 \fn QNativeIpcKey::legacyDefaultTypeForOs() noexcept
366
367 Returns the \l{Type} that corresponds to the native IPC key that
368 \l{QSharedMemory} and \l{QSystemSemaphore} used to use prior to Qt 6.6.
369 Applications and libraries that must retain compatibility with code using
370 either class that was compiled with Qt prior to version 6.6 can use this
371 function to determine what IPC type the other applications may be using.
372
373 Note that this function relies on Qt having been built with identical
374 configure-time options.
375*/
376#if defined(Q_OS_DARWIN)
377QNativeIpcKey::Type QNativeIpcKey::defaultTypeForOs_internal() noexcept
378{
379 if (qt_apple_isSandboxed())
380 return Type::PosixRealtime;
381 return Type::SystemV;
382}
383#endif
384
385/*!
386 \fn QNativeIpcKey::QNativeIpcKey() noexcept
387
388 Constructs a QNativeIpcKey object of type \l DefaultTypeForOs with an empty key.
389*/
390
391/*!
392 \fn QNativeIpcKey::QNativeIpcKey(Type type) noexcept
393 \fn QNativeIpcKey::QNativeIpcKey(const QString &key, Type type)
394
395 Constructs a QNativeIpcKey object holding native key \a key (or empty on
396 the overload without the parameter) for type \a type.
397*/
398
399/*!
400 \fn QNativeIpcKey::QNativeIpcKey(const QNativeIpcKey &other)
401 \fn QNativeIpcKey::QNativeIpcKey(QNativeIpcKey &&other) noexcept
402 \fn QNativeIpcKey &QNativeIpcKey::operator=(const QNativeIpcKey &other)
403 \fn QNativeIpcKey &QNativeIpcKey::operator=(QNativeIpcKey &&other) noexcept
404
405 Copies or moves the content of \a other.
406*/
407void QNativeIpcKey::copy_internal(const QNativeIpcKey &other)
408{
409 d = new QNativeIpcKeyPrivate(*other.d);
410}
411
412void QNativeIpcKey::move_internal(QNativeIpcKey &&) noexcept
413{
414 // inline code already moved properly, nothing for us to do here
415}
416
417QNativeIpcKey &QNativeIpcKey::assign_internal(const QNativeIpcKey &other)
418{
419 Q_ASSERT(d || other.d); // only 3 cases to handle
420 if (d && !other.d)
421 *d = {};
422 else if (d)
423 *d = *other.d;
424 else
425 d = new QNativeIpcKeyPrivate(*other.d);
426 return *this;
427}
428
429/*!
430 \fn QNativeIpcKey::~QNativeIpcKey()
431
432 Disposes of this QNativeIpcKey object.
433*/
434void QNativeIpcKey::destroy_internal() noexcept
435{
436 delete d;
437}
438
439/*!
440 \fn QNativeIpcKey::swap(QNativeIpcKey &other) noexcept
441 \memberswap{native IPC key and type}
442*/
443
444/*!
445 \fn swap(QNativeIpcKey &value1, QNativeIpcKey &value2) noexcept
446 \relates QNativeIpcKey
447
448 Swaps the native IPC key and type \a value1 with \a value2.
449 This operation is very fast and never fails.
450*/
451
452/*!
453 \fn QNativeIpcKey::isEmpty() const
454
455 Returns true if the nativeKey() is empty.
456
457 \sa nativeKey()
458*/
459
460/*!
461 \fn QNativeIpcKey::isValid() const
462
463 Returns true if this object contains a valid native IPC key type. Invalid
464 types are usually the result of a failure to parse a string representation
465 using fromString().
466
467 This function performs no check on the whether the key string is actually
468 supported or valid for the current operating system.
469
470 \sa type(), fromString()
471*/
472
473/*!
474 \fn QNativeIpcKey::type() const noexcept
475
476 Returns the key type associated with this object.
477
478 \sa nativeKey(), setType()
479*/
480
481/*!
482 \fn QNativeIpcKey::setType(Type type)
483
484 Sets the IPC type of this object to \a type.
485
486 \sa type(), setNativeKey()
487*/
488void QNativeIpcKey::setType_internal(Type type)
489{
490 Q_UNUSED(type);
491}
492
493/*!
494 \fn QNativeIpcKey::nativeKey() const noexcept
495
496 Returns the native key string associated with this object.
497
498 \sa setNativeKey(), type()
499*/
500
501/*!
502 \fn QNativeIpcKey::setNativeKey(const QString &newKey)
503
504 Sets the native key for this object to \a newKey.
505
506 \sa nativeKey(), setType()
507*/
508void QNativeIpcKey::setNativeKey_internal(const QString &)
509{
510 d->legacyKey_.clear();
511}
512
513/*!
514 \fn size_t QNativeIpcKey::qHash(const QNativeIpcKey &key, size_t seed)
515 \qhash{QNativeIpcKey}
516*/
517size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept
518{
519 // by *choice*, we're not including d->legacyKey_ in the hash -- it's
520 // already partially encoded in the key
521 return qHashMulti(seed, ipcKey.key, ipcKey.type());
522}
523
524/*!
525 \fn bool QNativeIpcKey::operator==(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
526 \fn bool QNativeIpcKey::operator!=(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
527
528 Returns true if the \a lhs and \a rhs objects hold the same (or different) contents.
529*/
530int QNativeIpcKey::compare_internal(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
531{
532 return (QNativeIpcKeyPrivate::legacyKey(lhs) == QNativeIpcKeyPrivate::legacyKey(rhs)) ? 0 : 1;
533}
534
535/*!
536 Returns the string representation of this object. String representations
537 are useful to inform other processes of the key this process created and
538 that they should attach to.
539
540 This function returns a null string if the current object is
541 \l{isValid()}{invalid}.
542
543 \sa fromString()
544*/
545QString QNativeIpcKey::toString() const
546{
547 QString prefix = typeToString(type());
548 if (prefix.isEmpty()) {
549 Q_ASSERT(prefix.isNull());
550 return prefix;
551 }
552
553 QString copy = nativeKey();
554 copy.replace(u'%', "%25"_L1);
555 if (copy.startsWith("//"_L1))
556 copy.replace(0, 2, u"/%2F"_s); // ensure it's parsed as a URL path
557
558 QUrl u;
559 u.setScheme(prefix);
560 u.setPath(copy, QUrl::TolerantMode);
561 if (isSlowPath()) {
562 QUrlQuery q;
563 if (!d->legacyKey_.isEmpty())
564 q.addQueryItem(u"legacyKey"_s, QString(d->legacyKey_).replace(u'%', "%25"_L1));
565 u.setQuery(q);
566 }
567 return u.toString(QUrl::DecodeReserved);
568}
569
570/*!
571 Parses the string form \a text and returns the corresponding QNativeIpcKey.
572 String representations are useful to inform other processes of the key this
573 process created and they should attach to.
574
575 If the string could not be parsed, this function returns an
576 \l{isValid()}{invalid} object.
577
578 \sa toString(), isValid()
579*/
580QNativeIpcKey QNativeIpcKey::fromString(const QString &text)
581{
582 QUrl u(text, QUrl::TolerantMode);
583 Type invalidType = {};
584 Type type = stringToType(u.scheme());
585 if (type == invalidType || !u.isValid() || !u.userInfo().isEmpty() || !u.host().isEmpty()
586 || u.port() != -1)
587 return QNativeIpcKey(invalidType);
588
589 QNativeIpcKey result(QString(), type);
590 if (result.type() != type) // range check, just in case
591 return QNativeIpcKey(invalidType);
592
593 // decode the payload
594 result.setNativeKey(u.path());
595
596 if (u.hasQuery()) {
597 const QList items = QUrlQuery(u).queryItems();
598 for (const auto &item : items) {
599 if (item.first == u"legacyKey"_s) {
600 QString legacyKey = QUrl::fromPercentEncoding(item.second.toUtf8());
601 QNativeIpcKeyPrivate::setLegacyKey(result, std::move(legacyKey));
602 } else {
603 // unknown query item
604 return QNativeIpcKey(invalidType);
605 }
606 }
607 }
608 return result;
609}
610
611QT_END_NAMESPACE
612
613#include "moc_qtipccommon.cpp"
614
615#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)