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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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