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
qsharedmemory.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
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
7
10
11#include <q20memory.h>
12#include <qdebug.h>
13#ifdef Q_OS_WIN
14# include <qt_windows.h>
15#endif
16#include <errno.h>
17
18#ifndef MAX_PATH
19# ifdef PATH_MAX
20# define MAX_PATH PATH_MAX
21# else
22# define MAX_PATH 1024
23# endif
24#endif
25
27
28#if QT_CONFIG(sharedmemory)
29
30using namespace QtIpcCommon;
31using namespace Qt::StringLiterals;
32
33QSharedMemoryPrivate::~QSharedMemoryPrivate()
34{
35 destructBackend();
36}
37
38inline void QSharedMemoryPrivate::constructBackend()
39{
40 using namespace q20;
41 visit([](auto p) { construct_at(p); });
42}
43
44inline void QSharedMemoryPrivate::destructBackend()
45{
46 visit([](auto p) { std::destroy_at(p); });
47}
48
49#if QT_CONFIG(systemsemaphore)
50inline QNativeIpcKey QSharedMemoryPrivate::semaphoreNativeKey() const
51{
52 if (isIpcSupported(IpcType::SharedMemory, QNativeIpcKey::Type::Windows)
53 && nativeKey.type() == QNativeIpcKey::Type::Windows) {
54 // native keys are plain kernel object names, limited to MAX_PATH
55 auto suffix = "_sem"_L1;
56 QString semkey = nativeKey.nativeKey();
57 semkey.truncate(MAX_PATH - suffix.size() - 1);
58 semkey += suffix;
59 return { semkey, QNativeIpcKey::Type::Windows };
60 }
61
62 // System V and POSIX keys appear to operate in different namespaces, so we
63 // can just use the same native key
64 return nativeKey;
65}
66#endif
67
68/*!
69 \class QSharedMemory
70 \inmodule QtCore
71 \since 4.4
72
73 \brief The QSharedMemory class provides access to a shared memory segment.
74
75 QSharedMemory provides access to a \l{Shared Memory}{shared memory segment}
76 by multiple threads and processes. Shared memory segments are identified by a
77 key, represented by \l QNativeIpcKey. A key can be created in a
78 cross-platform manner by using platformSafeKey().
79
80 One QSharedMemory object must create() the segment and this call specifies
81 the size of the segment. All other processes simply attach() to the segment
82 that must already exist. After either operation is successful, the
83 application may call data() to obtain a pointer to the data.
84
85 To support non-atomic operations, QSharedMemory provides API to gain
86 exclusive access: you may lock the shared memory with lock() before reading
87 from or writing to the shared memory, but remember to release the lock with
88 unlock() after you are done.
89
90 By default, QSharedMemory automatically destroys the shared memory segment
91 when the last instance of QSharedMemory is \l{detach()}{detached} from the
92 segment, and no references to the segment remain.
93
94 For details on the key types, platform-specific limitations, and
95 interoperability with older or non-Qt applications, see the \l{Native IPC
96 Keys} documentation. That includes important information for sandboxed
97 applications on Apple platforms, including all apps obtained via the Apple
98 App Store.
99
100 \sa {Inter-Process Communication}, QSystemSemaphore
101 */
102
103/*!
104 \overload QSharedMemory()
105
106 Constructs a shared memory object with the given \a parent. The shared memory
107 object's key is not set by the constructor, so the shared memory object does
108 not have an underlying shared memory segment attached. The key must be set
109 with setNativeKey() before create() or attach() can be used.
110
111 \sa setNativeKey()
112 */
113
114QSharedMemory::QSharedMemory(QObject *parent)
115 : QSharedMemory(QNativeIpcKey(), parent)
116{
117}
118
119/*!
120 \overload
121
122 Constructs a shared memory object with the given \a parent and with
123 its key set to \a key. Because its key is set, its create() and
124 attach() functions can be called.
125
126 \sa setNativeKey(), create(), attach()
127 */
128QSharedMemory::QSharedMemory(const QNativeIpcKey &key, QObject *parent)
129 : QObject(*new QSharedMemoryPrivate(key.type()), parent)
130{
131 setNativeKey(key);
132}
133
134/*!
135 Constructs a shared memory object with the given \a parent and with
136 the legacy key set to \a key. Because its key is set, its create() and
137 attach() functions can be called.
138
139 \sa setKey(), create(), attach()
140 */
141QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
142 : QSharedMemory(legacyNativeKey(key), parent)
143{
144}
145
146/*!
147 The destructor clears the key, which forces the shared memory object
148 to \l {detach()} {detach} from its underlying shared memory
149 segment. If this shared memory object is the last one connected to
150 the shared memory segment, the detach() operation destroys the
151 shared memory segment.
152
153 \sa detach(), isAttached()
154 */
155QSharedMemory::~QSharedMemory()
156{
157 Q_D(QSharedMemory);
158 if (isAttached())
159 detach();
160 d->cleanHandle();
161}
162
163/*!
164 \overload
165
166 Sets the legacy \a key for this shared memory object. If \a key is the same
167 as the current key, the function returns without doing anything. Otherwise,
168 if the shared memory object is attached to an underlying shared memory
169 segment, it will \l {detach()} {detach} from it before setting the new key.
170 This function does not do an attach().
171
172 You can call key() to retrieve the legacy key. This function is mostly the
173 same as:
174
175 \code
176 shm.setNativeKey(QSharedMemory::legacyNativeKey(key));
177 \endcode
178
179 except that it enables obtaining the legacy key using key().
180
181 \sa key(), nativeKey(), isAttached()
182*/
183void QSharedMemory::setKey(const QString &key)
184{
185 setNativeKey(legacyNativeKey(key));
186}
187
188/*!
189 \since 4.8
190 \fn void QSharedMemory::setNativeKey(const QString &key, QNativeIpcKey::Type type)
191
192 Sets the native, platform specific, \a key for this shared memory object of
193 type \a type (the type parameter has been available since Qt 6.6). If \a key
194 is the same as the current native key, the function returns without doing
195 anything. Otherwise, if the shared memory object is attached to an underlying
196 shared memory segment, it will \l {detach()} {detach} from it before setting
197 the new key. This function does not do an attach().
198
199 This function is useful if the native key was shared from another process,
200 though the application must take care to ensure the key type matches what the
201 other process expects. See \l{Native IPC Keys} for more information.
202
203 Portable native keys can be obtained using platformSafeKey().
204
205 You can call nativeKey() to retrieve the native key.
206
207 \sa nativeKey(), nativeIpcKey(), isAttached()
208*/
209
210/*!
211 \since 6.6
212
213 Sets the native, platform specific, \a key for this shared memory object. If
214 \a key is the same as the current native key, the function returns without
215 doing anything. Otherwise, if the shared memory object is attached to an
216 underlying shared memory segment, it will \l {detach()} {detach} from it
217 before setting the new key. This function does not do an attach().
218
219 This function is useful if the native key was shared from another process.
220 See \l{Native IPC Keys} for more information.
221
222 Portable native keys can be obtained using platformSafeKey().
223
224 You can call nativeKey() to retrieve the native key.
225
226 \sa nativeKey(), nativeIpcKey(), isAttached()
227*/
228void QSharedMemory::setNativeKey(const QNativeIpcKey &key)
229{
230 Q_D(QSharedMemory);
231 if (key == d->nativeKey && key.isEmpty())
232 return;
233 if (!isKeyTypeSupported(key.type())) {
234 d->setError(KeyError, tr("%1: unsupported key type")
235 .arg("QSharedMemory::setNativeKey"_L1));
236 return;
237 }
238
239 if (isAttached())
240 detach();
241 d->cleanHandle();
242 if (key.type() == d->nativeKey.type()) {
243 // we can reuse the backend
244 d->nativeKey = key;
245 } else {
246 // we must recreate the backend
247 d->destructBackend();
248 d->nativeKey = key;
249 d->constructBackend();
250 }
251}
252
253bool QSharedMemoryPrivate::initKey(SemaphoreAccessMode mode)
254{
255 if (!cleanHandle())
256 return false;
257#if QT_CONFIG(systemsemaphore)
258 const QString legacyKey = QNativeIpcKeyPrivate::legacyKey(nativeKey);
259 const QNativeIpcKey semKey = legacyKey.isEmpty()
260 ? semaphoreNativeKey()
261 : QSystemSemaphore::legacyNativeKey(legacyKey, nativeKey.type());
262 systemSemaphore.setNativeKey(semKey, 1, mode);
263 if (systemSemaphore.error() != QSystemSemaphore::NoError) {
264 QString function = "QSharedMemoryPrivate::initKey"_L1;
265 errorString = QSharedMemory::tr("%1: unable to set key on lock (%2)")
266 .arg(function, systemSemaphore.errorString());
267 switch(systemSemaphore.error()) {
268 case QSystemSemaphore::PermissionDenied:
269 error = QSharedMemory::PermissionDenied;
270 break;
271 case QSystemSemaphore::KeyError:
272 error = QSharedMemory::KeyError;
273 break;
274 case QSystemSemaphore::AlreadyExists:
275 error = QSharedMemory::AlreadyExists;
276 break;
277 case QSystemSemaphore::NotFound:
278 error = QSharedMemory::NotFound;
279 break;
280 case QSystemSemaphore::OutOfResources:
281 error = QSharedMemory::OutOfResources;
282 break;
283 case QSystemSemaphore::UnknownError:
284 default:
285 error = QSharedMemory::UnknownError;
286 break;
287 }
288 return false;
289 }
290#else
291 Q_UNUSED(mode);
292#endif
293 errorString = QString();
294 error = QSharedMemory::NoError;
295 return true;
296}
297
298/*!
299 Returns the legacy key assigned with setKey() to this shared memory, or a null key
300 if no key has been assigned, or if the segment is using a nativeKey(). The
301 key is the identifier used by Qt applications to identify the shared memory
302 segment.
303
304 You can find the native, platform specific, key used by the operating system
305 by calling nativeKey().
306
307 \sa setKey(), setNativeKey()
308 */
309QString QSharedMemory::key() const
310{
311 Q_D(const QSharedMemory);
312 return QNativeIpcKeyPrivate::legacyKey(d->nativeKey);
313}
314
315/*!
316 \since 4.8
317
318 Returns the native, platform specific, key for this shared memory object. The
319 native key is the identifier used by the operating system to identify the
320 shared memory segment.
321
322 You can use the native key to access shared memory segments that have not
323 been created by Qt, or to grant shared memory access to non-Qt applications.
324 See \l{Native IPC Keys} for more information.
325
326 \sa setNativeKey(), nativeIpcKey()
327*/
328QString QSharedMemory::nativeKey() const
329{
330 Q_D(const QSharedMemory);
331 return d->nativeKey.nativeKey();
332}
333
334/*!
335 \since 6.6
336
337 Returns the key type for this shared memory object. The key type complements
338 the nativeKey() as the identifier used by the operating system to identify
339 the shared memory segment.
340
341 You can use the native key to access shared memory segments that have not
342 been created by Qt, or to grant shared memory access to non-Qt applications.
343 See \l{Native IPC Keys} for more information.
344
345 \sa nativeKey(), setNativeKey()
346*/
347QNativeIpcKey QSharedMemory::nativeIpcKey() const
348{
349 Q_D(const QSharedMemory);
350 return d->nativeKey;
351}
352
353/*!
354 Creates a shared memory segment of \a size bytes with the key passed to the
355 constructor or set with setNativeKey(), then attaches to
356 the new shared memory segment with the given access \a mode and returns
357 \tt true. If a shared memory segment identified by the key already exists,
358 the attach operation is not performed and \tt false is returned. When the
359 return value is \tt false, call error() to determine which error occurred.
360
361 \sa error()
362 */
363bool QSharedMemory::create(qsizetype size, AccessMode mode)
364{
365 Q_D(QSharedMemory);
366 QLatin1StringView function = "QSharedMemory::create"_L1;
367
368#if QT_CONFIG(systemsemaphore)
369 if (!d->initKey(QSystemSemaphore::Create))
370 return false;
371 QSharedMemoryLocker lock(this);
372 if (!d->nativeKey.isEmpty() && !d->tryLocker(&lock, function))
373 return false;
374#else
375 if (!d->initKey({}))
376 return false;
377#endif
378
379 if (size <= 0) {
380 d->error = QSharedMemory::InvalidSize;
381 d->errorString =
382 QSharedMemory::tr("%1: create size is less then 0").arg(function);
383 return false;
384 }
385
386 if (!d->create(size))
387 return false;
388
389 return d->attach(mode);
390}
391
392/*!
393 Returns the size of the attached shared memory segment. If no shared
394 memory segment is attached, 0 is returned.
395
396 \note The size of the segment may be larger than the requested size that was
397 passed to create().
398
399 \sa create(), attach()
400 */
401qsizetype QSharedMemory::size() const
402{
403 Q_D(const QSharedMemory);
404 return d->size;
405}
406
407/*!
408 \enum QSharedMemory::AccessMode
409
410 \value ReadOnly The shared memory segment is read-only. Writing to
411 the shared memory segment is not allowed. An attempt to write to a
412 shared memory segment created with ReadOnly causes the program to
413 abort.
414
415 \value ReadWrite Reading and writing the shared memory segment are
416 both allowed.
417*/
418
419/*!
420 Attempts to attach the process to the shared memory segment
421 identified by the key that was passed to the constructor or to a
422 call to setNativeKey(). The access \a mode is \l {QSharedMemory::}
423 {ReadWrite} by default. It can also be \l {QSharedMemory::}
424 {ReadOnly}. Returns \c true if the attach operation is successful. If
425 false is returned, call error() to determine which error occurred.
426 After attaching the shared memory segment, a pointer to the shared
427 memory can be obtained by calling data().
428
429 \sa isAttached(), detach(), create()
430 */
431bool QSharedMemory::attach(AccessMode mode)
432{
433 Q_D(QSharedMemory);
434
435 if (isAttached() || !d->initKey({}))
436 return false;
437#if QT_CONFIG(systemsemaphore)
438 QSharedMemoryLocker lock(this);
439 if (!d->nativeKey.isEmpty() && !d->tryLocker(&lock, "QSharedMemory::attach"_L1))
440 return false;
441#endif
442
443 if (isAttached() || !d->handle())
444 return false;
445
446 return d->attach(mode);
447}
448
449/*!
450 Returns \c true if this process is attached to the shared memory
451 segment.
452
453 \sa attach(), detach()
454 */
455bool QSharedMemory::isAttached() const
456{
457 Q_D(const QSharedMemory);
458 return (nullptr != d->memory);
459}
460
461/*!
462 Detaches the process from the shared memory segment. If this was the
463 last process attached to the shared memory segment, then the shared
464 memory segment is released by the system, i.e., the contents are
465 destroyed. The function returns \c true if it detaches the shared
466 memory segment. If it returns \c false, it usually means the segment
467 either isn't attached, or it is locked by another process.
468
469 \sa attach(), isAttached()
470 */
471bool QSharedMemory::detach()
472{
473 Q_D(QSharedMemory);
474 if (!isAttached())
475 return false;
476
477#if QT_CONFIG(systemsemaphore)
478 QSharedMemoryLocker lock(this);
479 if (!d->nativeKey.isEmpty() && !d->tryLocker(&lock, "QSharedMemory::detach"_L1))
480 return false;
481#endif
482
483 return d->detach();
484}
485
486/*!
487 Returns a pointer to the contents of the shared memory segment, if one is
488 attached. Otherwise it returns null. The value returned by this function will
489 not change until a \l {detach()}{detach} happens, so it is safe to store this
490 pointer.
491
492 If the memory operations are not atomic, you may lock the shared memory with
493 lock() before reading from or writing, but remember to release the lock with
494 unlock() after you are done.
495
496 \sa attach()
497 */
498void *QSharedMemory::data()
499{
500 Q_D(QSharedMemory);
501 return d->memory;
502}
503
504/*!
505 Returns a const pointer to the contents of the shared memory segment, if one
506 is attached. Otherwise it returns null. The value returned by this function
507 will not change until a \l {detach()}{detach} happens, so it is safe to store
508 this pointer.
509
510 If the memory operations are not atomic, you may lock the shared memory with
511 lock() before reading from or writing, but remember to release the lock with
512 unlock() after you are done.
513
514 \sa attach(), create()
515 */
516const void *QSharedMemory::constData() const
517{
518 Q_D(const QSharedMemory);
519 return d->memory;
520}
521
522/*!
523 \overload data()
524 */
525const void *QSharedMemory::data() const
526{
527 Q_D(const QSharedMemory);
528 return d->memory;
529}
530
531#if QT_CONFIG(systemsemaphore)
532/*!
533 This is a semaphore that locks the shared memory segment for access
534 by this process and returns \c true. If another process has locked the
535 segment, this function blocks until the lock is released. Then it
536 acquires the lock and returns \c true. If this function returns \c false,
537 it means that you have ignored a false return from create() or attach(),
538 that you have set the key with setNativeKey() or that
539 QSystemSemaphore::acquire() failed due to an unknown system error.
540
541 \sa unlock(), data(), QSystemSemaphore::acquire()
542 */
543bool QSharedMemory::lock()
544{
545 Q_D(QSharedMemory);
546 if (d->lockedByMe) {
547 qWarning("QSharedMemory::lock: already locked");
548 return true;
549 }
550 if (d->systemSemaphore.acquire()) {
551 d->lockedByMe = true;
552 return true;
553 }
554 const auto function = "QSharedMemory::lock"_L1;
555 d->errorString = QSharedMemory::tr("%1: unable to lock").arg(function);
556 d->error = QSharedMemory::LockError;
557 return false;
558}
559
560/*!
561 Releases the lock on the shared memory segment and returns \c true, if
562 the lock is currently held by this process. If the segment is not
563 locked, or if the lock is held by another process, nothing happens
564 and false is returned.
565
566 \sa lock()
567 */
568bool QSharedMemory::unlock()
569{
570 Q_D(QSharedMemory);
571 if (!d->lockedByMe)
572 return false;
573 d->lockedByMe = false;
574 if (d->systemSemaphore.release())
575 return true;
576 const auto function = "QSharedMemory::unlock"_L1;
577 d->errorString = QSharedMemory::tr("%1: unable to unlock").arg(function);
578 d->error = QSharedMemory::LockError;
579 return false;
580}
581#endif // QT_CONFIG(systemsemaphore)
582
583/*!
584 \enum QSharedMemory::SharedMemoryError
585
586 \value NoError No error occurred.
587
588 \value PermissionDenied The operation failed because the caller
589 didn't have the required permissions.
590
591 \value InvalidSize A create operation failed because the requested
592 size was invalid.
593
594 \value KeyError The operation failed because of an invalid key.
595
596 \value AlreadyExists A create() operation failed because a shared
597 memory segment with the specified key already existed.
598
599 \value NotFound An attach() failed because a shared memory segment
600 with the specified key could not be found.
601
602 \value LockError The attempt to lock() the shared memory segment
603 failed because create() or attach() failed and returned false, or
604 because a system error occurred in QSystemSemaphore::acquire().
605
606 \value OutOfResources A create() operation failed because there was
607 not enough memory available to fill the request.
608
609 \value UnknownError Something else happened and it was bad.
610*/
611
612/*!
613 Returns a value indicating whether an error occurred, and, if so,
614 which error it was.
615
616 \sa errorString()
617 */
618QSharedMemory::SharedMemoryError QSharedMemory::error() const
619{
620 Q_D(const QSharedMemory);
621 return d->error;
622}
623
624/*!
625 Returns a text description of the last error that occurred. If
626 error() returns an \l {QSharedMemory::SharedMemoryError} {error
627 value}, call this function to get a text string that describes the
628 error.
629
630 \sa error()
631 */
632QString QSharedMemory::errorString() const
633{
634 Q_D(const QSharedMemory);
635 return d->errorString;
636}
637
638void QSharedMemoryPrivate::setUnixErrorString(QLatin1StringView function)
639{
640 // EINVAL is handled in functions so they can give better error strings
641 switch (errno) {
642 case EACCES:
643 errorString = QSharedMemory::tr("%1: permission denied").arg(function);
644 error = QSharedMemory::PermissionDenied;
645 break;
646 case EEXIST:
647 errorString = QSharedMemory::tr("%1: already exists").arg(function);
648 error = QSharedMemory::AlreadyExists;
649 break;
650 case ENOENT:
651 errorString = QSharedMemory::tr("%1: doesn't exist").arg(function);
652 error = QSharedMemory::NotFound;
653 break;
654 case EMFILE:
655 case ENOMEM:
656 case ENOSPC:
657 errorString = QSharedMemory::tr("%1: out of resources").arg(function);
658 error = QSharedMemory::OutOfResources;
659 break;
660 default:
661 errorString = QSharedMemory::tr("%1: unknown error: %2")
662 .arg(function, qt_error_string(errno));
663 error = QSharedMemory::UnknownError;
664#if defined QSHAREDMEMORY_DEBUG
665 qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
666#endif
667 }
668}
669
670bool QSharedMemory::isKeyTypeSupported(QNativeIpcKey::Type type)
671{
672 if (!isIpcSupported(IpcType::SharedMemory, type))
673 return false;
674 using Variant = decltype(QSharedMemoryPrivate::backend);
675 return Variant::staticVisit(type, [](auto ptr) {
676 using Impl = std::decay_t<decltype(*ptr)>;
677 return Impl::runtimeSupportCheck();
678 });
679}
680
681QNativeIpcKey QSharedMemory::platformSafeKey(const QString &key, QNativeIpcKey::Type type)
682{
683 return QtIpcCommon::platformSafeKey(key, IpcType::SharedMemory, type);
684}
685
686QNativeIpcKey QSharedMemory::legacyNativeKey(const QString &key, QNativeIpcKey::Type type)
687{
688 return QtIpcCommon::legacyPlatformSafeKey(key, IpcType::SharedMemory, type);
689}
690
691#endif // QT_CONFIG(sharedmemory)
692
693QT_END_NAMESPACE
694
695#include "moc_qsharedmemory.cpp"