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

source code of qtbase/src/corelib/ipc/qtipccommon.cpp