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 k.setNativeKey(u'/' + key);
195#endif
196 }
197 return k;
198
199 case QNativeIpcKey::Type::Windows:
200 if (isIpcSupported(ipcType, QNativeIpcKey::Type::Windows)) {
201 QStringView prefix;
202 QStringView payload = key;
203 // see https://learn.microsoft.com/en-us/windows/win32/termserv/kernel-object-namespaces
204 for (QStringView candidate : { u"Local\\"_sv, u"Global\\"_sv }) {
205 if (!key.startsWith(candidate))
206 continue;
207 prefix = candidate;
208 payload = payload.sliced(prefix.size());
209 break;
210 }
211
212 QStringView mid;
213 switch (ipcType) {
214 case IpcType::SharedMemory: mid = u"shm_"; break;
215 case IpcType::SystemSemaphore: mid = u"sem_"; break;
216 }
217
218 QString result = prefix + mid + payload;
219#ifdef Q_OS_WINDOWS
220 result.truncate(MAX_PATH);
221#endif
222 k.setNativeKey(result);
223 }
224 return k;
225
226 case QNativeIpcKey::Type::SystemV:
227 break;
228 }
229
230 // System V
231 if (isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) {
232 if (key.startsWith(u'/')) {
233 k.setNativeKey(key);
234 } else {
235 QString baseDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
236 k.setNativeKey(baseDir + u'/' + key);
237 }
238 }
239 return k;
240}
241
242/*!
243 \class QNativeIpcKey
244 \inmodule QtCore
245 \since 6.6
246 \brief The QNativeIpcKey class holds a native key used by QSystemSemaphore and QSharedMemory.
247
248 \compares equality
249
250 The \l QSharedMemory and \l QSystemSemaphore classes identify their
251 resource using a system-wide identifier known as a "key". The low-level key
252 value as well as the key type are encapsulated in Qt using the \l
253 QNativeIpcKey class.
254
255 Those two classes also provide the means to create native keys from a
256 cross-platform identifier, using QSharedMemory::platformSafeKey() and
257 QSystemSemaphore::platformSafeKey(). Applications should never share the
258 input to those functions, as different versions of Qt may perform different
259 transformations, resulting in different native keys. Instead, the
260 application that created the IPC object should communicate the resulting
261 native key using the methods described below.
262
263 For details on the key types, platform-specific limitations, and
264 interoperability with older or non-Qt applications, see the \l{Native IPC
265 Keys} documentation. That includes important information for sandboxed
266 applications on Apple platforms, including all apps obtained via the Apple
267 App Store.
268
269 \section1 Communicating keys to other processes
270 \section2 Communicating keys to other Qt processes
271
272 If the other process supports QNativeIpcKey, the best way of communicating
273 is via the string representation obtained from toString() and parsing it
274 using fromString(). This representation can be stored on a file whose name
275 is well-known or passed on the command-line to a child process using
276 QProcess::setArguments().
277
278 If the other process does not support QNativeIpcKey, then the two processes
279 can exchange the nativeKey() but the older code is likely unable to adjust
280 its key type. The legacyDefaultTypeForOs() function returns the type that
281 legacy code used, which may not match the \l{DefaultTypeForOs} constant.
282 This is still true even if the old application is not using the same build
283 as the new one (for example, it is a Qt 5 application), provided the
284 options passed to the Qt configure script are the same.
285
286 \section2 Communicating keys to non-Qt processes
287
288 When communicating with non-Qt processes, the application must arrange to
289 obtain the key type the other process is using. This is important
290 particularly on Unix systems, where both \l PosixRealtime and \l SystemV
291 are common.
292
293 \section1 String representation of native keys
294
295 The format of the string representation of a QNativeIpcKey is meant to be
296 stable and therefore backwards and forwards compatible, provided the key
297 type is supported by the Qt version in question. That is to say, an older
298 Qt will fail to parse the string representation of a key type introduced
299 after it was released. However, successfully parsing a string
300 representation does not imply the Qt classes can successfully create an
301 object of that type; applications should verify support using
302 QSharedMemory::isKeyTypeSupported() and QSystemSemaphore::isKeyTypeSupported().
303
304 The format of the string representation is formed by two components,
305 separated by a colon (':'). The first component is the key type, described
306 in the table below. The second component is a type-specific payload, using
307 \l{QByteArray::fromPercentEncoding}{percent-encoding}. For all currently
308 supported key types, the decoded form is identical to the contents of the
309 nativeKey() field.
310
311 \table
312 \row \li Key type \li String representation
313 \row \li \l PosixRealtime \li \c "posix"
314 \row \li \l SystemV \li \c "systemv"
315 \row \li \l Windows \li \c "windows"
316 \row \li Non-standard SystemV \li \c "systemv-" followed by a decimal number
317 \endtable
318
319 This format resembles a URI and allows parsing using URI/URL-parsing
320 functions, such as \l QUrl. When parsed by such API, the key type will show
321 up as the \l{QUrl::scheme()}{scheme}, and the payload will be the
322 \l{QUrl::path()}{path}. Use of query or fragments is reserved.
323
324 \sa QSharedMemory, QSystemSemaphore
325*/
326
327/*!
328 \enum QNativeIpcKey::Type
329
330 This enum describes the backend type for the IPC object. For details on the
331 key types, see the \l{Native IPC Keys} documentation.
332
333 \value SystemV X/Open System Initiative (XSI) or System V (SVr4) API
334 \value PosixRealtime IEEE 1003.1b (POSIX.1b) API
335 \value Windows Win32 API
336
337 \sa setType(), type()
338*/
339
340/*!
341 \variable QNativeIpcKey::DefaultTypeForOs
342
343 This constant expression variable holds the default native IPC type for the
344 current OS. It will be Type::Windows for Windows systems and
345 Type::PosixRealtime elsewhere. Note that this constant is different from
346 what \l QSharedMemory and \l QSystemSemaphore defaulted to on the majority
347 of Unix systems prior to Qt 6.6; see legacyDefaultTypeForOs() for more
348 information.
349*/
350
351/*!
352 \fn QNativeIpcKey::legacyDefaultTypeForOs() noexcept
353
354 Returns the \l{Type} that corresponds to the native IPC key that
355 \l{QSharedMemory} and \l{QSystemSemaphore} used to use prior to Qt 6.6.
356 Applications and libraries that must retain compatibility with code using
357 either class that was compiled with Qt prior to version 6.6 can use this
358 function to determine what IPC type the other applications may be using.
359
360 Note that this function relies on Qt having been built with identical
361 configure-time options.
362*/
363#if defined(Q_OS_DARWIN)
364QNativeIpcKey::Type QNativeIpcKey::defaultTypeForOs_internal() noexcept
365{
366 if (qt_apple_isSandboxed())
367 return Type::PosixRealtime;
368 return Type::SystemV;
369}
370#endif
371
372/*!
373 \fn QNativeIpcKey::QNativeIpcKey() noexcept
374
375 Constructs a QNativeIpcKey object of type \l DefaultTypeForOs with an empty key.
376*/
377
378/*!
379 \fn QNativeIpcKey::QNativeIpcKey(Type type) noexcept
380 \fn QNativeIpcKey::QNativeIpcKey(const QString &key, Type type)
381
382 Constructs a QNativeIpcKey object holding native key \a key (or empty on
383 the overload without the parameter) for type \a type.
384*/
385
386/*!
387 \fn QNativeIpcKey::QNativeIpcKey(const QNativeIpcKey &other)
388 \fn QNativeIpcKey::QNativeIpcKey(QNativeIpcKey &&other) noexcept
389 \fn QNativeIpcKey &QNativeIpcKey::operator=(const QNativeIpcKey &other)
390 \fn QNativeIpcKey &QNativeIpcKey::operator=(QNativeIpcKey &&other) noexcept
391
392 Copies or moves the content of \a other.
393*/
394void QNativeIpcKey::copy_internal(const QNativeIpcKey &other)
395{
396 d = new QNativeIpcKeyPrivate(*other.d);
397}
398
399void QNativeIpcKey::move_internal(QNativeIpcKey &&) noexcept
400{
401 // inline code already moved properly, nothing for us to do here
402}
403
404QNativeIpcKey &QNativeIpcKey::assign_internal(const QNativeIpcKey &other)
405{
406 Q_ASSERT(d || other.d); // only 3 cases to handle
407 if (d && !other.d)
408 *d = {};
409 else if (d)
410 *d = *other.d;
411 else
412 d = new QNativeIpcKeyPrivate(*other.d);
413 return *this;
414}
415
416/*!
417 \fn QNativeIpcKey::~QNativeIpcKey()
418
419 Disposes of this QNativeIpcKey object.
420*/
421void QNativeIpcKey::destroy_internal() noexcept
422{
423 delete d;
424}
425
426/*!
427 \fn QNativeIpcKey::swap(QNativeIpcKey &other) noexcept
428 \memberswap{native IPC key and type}
429*/
430
431/*!
432 \fn swap(QNativeIpcKey &value1, QNativeIpcKey &value2) noexcept
433 \relates QNativeIpcKey
434
435 Swaps the native IPC key and type \a value1 with \a value2.
436 This operation is very fast and never fails.
437*/
438
439/*!
440 \fn QNativeIpcKey::isEmpty() const
441
442 Returns true if the nativeKey() is empty.
443
444 \sa nativeKey()
445*/
446
447/*!
448 \fn QNativeIpcKey::isValid() const
449
450 Returns true if this object contains a valid native IPC key type. Invalid
451 types are usually the result of a failure to parse a string representation
452 using fromString().
453
454 This function performs no check on the whether the key string is actually
455 supported or valid for the current operating system.
456
457 \sa type(), fromString()
458*/
459
460/*!
461 \fn QNativeIpcKey::type() const noexcept
462
463 Returns the key type associated with this object.
464
465 \sa nativeKey(), setType()
466*/
467
468/*!
469 \fn QNativeIpcKey::setType(Type type)
470
471 Sets the IPC type of this object to \a type.
472
473 \sa type(), setNativeKey()
474*/
475void QNativeIpcKey::setType_internal(Type type)
476{
477 Q_UNUSED(type);
478}
479
480/*!
481 \fn QNativeIpcKey::nativeKey() const noexcept
482
483 Returns the native key string associated with this object.
484
485 \sa setNativeKey(), type()
486*/
487
488/*!
489 \fn QNativeIpcKey::setNativeKey(const QString &newKey)
490
491 Sets the native key for this object to \a newKey.
492
493 \sa nativeKey(), setType()
494*/
495void QNativeIpcKey::setNativeKey_internal(const QString &)
496{
497 d->legacyKey_.clear();
498}
499
500/*!
501 \fn size_t QNativeIpcKey::qHash(const QNativeIpcKey &key, size_t seed)
502 \qhash{QNativeIpcKey}
503*/
504size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept
505{
506 // by *choice*, we're not including d->legacyKey_ in the hash -- it's
507 // already partially encoded in the key
508 return qHashMulti(seed, ipcKey.key, ipcKey.type());
509}
510
511/*!
512 \fn bool QNativeIpcKey::operator==(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
513 \fn bool QNativeIpcKey::operator!=(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
514
515 Returns true if the \a lhs and \a rhs objects hold the same (or different) contents.
516*/
517int QNativeIpcKey::compare_internal(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
518{
519 return (QNativeIpcKeyPrivate::legacyKey(lhs) == QNativeIpcKeyPrivate::legacyKey(rhs)) ? 0 : 1;
520}
521
522/*!
523 Returns the string representation of this object. String representations
524 are useful to inform other processes of the key this process created and
525 that they should attach to.
526
527 This function returns a null string if the current object is
528 \l{isValid()}{invalid}.
529
530 \sa fromString()
531*/
532QString QNativeIpcKey::toString() const
533{
534 QString prefix = typeToString(type());
535 if (prefix.isEmpty()) {
536 Q_ASSERT(prefix.isNull());
537 return prefix;
538 }
539
540 QString copy = nativeKey();
541 copy.replace(u'%', "%25"_L1);
542 if (copy.startsWith("//"_L1))
543 copy.replace(0, 2, u"/%2F"_s); // ensure it's parsed as a URL path
544
545 QUrl u;
546 u.setScheme(prefix);
547 u.setPath(copy, QUrl::TolerantMode);
548 if (isSlowPath()) {
549 QUrlQuery q;
550 if (!d->legacyKey_.isEmpty())
551 q.addQueryItem(u"legacyKey"_s, QString(d->legacyKey_).replace(u'%', "%25"_L1));
552 u.setQuery(q);
553 }
554 return u.toString(QUrl::DecodeReserved);
555}
556
557/*!
558 Parses the string form \a text and returns the corresponding QNativeIpcKey.
559 String representations are useful to inform other processes of the key this
560 process created and they should attach to.
561
562 If the string could not be parsed, this function returns an
563 \l{isValid()}{invalid} object.
564
565 \sa toString(), isValid()
566*/
567QNativeIpcKey QNativeIpcKey::fromString(const QString &text)
568{
569 QUrl u(text, QUrl::TolerantMode);
570 Type invalidType = {};
571 Type type = stringToType(u.scheme());
572 if (type == invalidType || !u.isValid() || !u.userInfo().isEmpty() || !u.host().isEmpty()
573 || u.port() != -1)
574 return QNativeIpcKey(invalidType);
575
576 QNativeIpcKey result(QString(), type);
577 if (result.type() != type) // range check, just in case
578 return QNativeIpcKey(invalidType);
579
580 // decode the payload
581 result.setNativeKey(u.path());
582
583 if (u.hasQuery()) {
584 const QList items = QUrlQuery(u).queryItems();
585 for (const auto &item : items) {
586 if (item.first == u"legacyKey"_s) {
587 QString legacyKey = QUrl::fromPercentEncoding(item.second.toUtf8());
588 QNativeIpcKeyPrivate::setLegacyKey(result, std::move(legacyKey));
589 } else {
590 // unknown query item
591 return QNativeIpcKey(invalidType);
592 }
593 }
594 }
595 return result;
596}
597
598QT_END_NAMESPACE
599
600#include "moc_qtipccommon.cpp"
601
602#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)