From 909112fa522a188373741797402445d22e3974b8 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 22 Sep 2022 14:38:22 -0700 Subject: QSharedMemory/doc: update docs to be more modern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - We don't support HPUX any more, so no need to talk about it - Clarify that it's the QSharedMemory destructor that releases the resources on Unix systems - Explain the System V and POSIX backends and how to detect them - Add a note about Android not being supported. - Add a section about using QFile for shared memory Change-Id: I413ea647c2a5453b8307fffd17174c8083416529 Reviewed-by: Tor Arne Vestbø Reviewed-by: Mårten Nordheim --- src/corelib/kernel/qsharedmemory.cpp | 112 +++++++++++++++++++++++++++++------ 1 file changed, 95 insertions(+), 17 deletions(-) (limited to 'src/corelib/kernel') diff --git a/src/corelib/kernel/qsharedmemory.cpp b/src/corelib/kernel/qsharedmemory.cpp index 79a5bfe1cb..2449564151 100644 --- a/src/corelib/kernel/qsharedmemory.cpp +++ b/src/corelib/kernel/qsharedmemory.cpp @@ -105,22 +105,34 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, \li Unix: QSharedMemory "owns" the shared memory segment. When the last thread or process that has an instance of QSharedMemory attached to a particular shared memory segment detaches from the - segment by destroying its instance of QSharedMemory, the Unix kernel - release the shared memory segment. But if that last thread or + segment by destroying its instance of QSharedMemory, the destructor + releases the shared memory segment. But if that last thread or process crashes without running the QSharedMemory destructor, the shared memory segment survives the crash. - \li HP-UX: Only one attach to a shared memory segment is allowed per - process. This means that QSharedMemory should not be used across - multiple threads in the same process in HP-UX. + \li Unix: QSharedMemory can be implemented by one of two different + backends, selected at Qt build time: System V or POSIX. Qt defaults to + using the System V API if it is available, and POSIX if not. These two + backends do not interoperate, so two applications must ensure they use the + same one, even if the native key (see setNativeKey()) is the same. - \li Apple platforms: Sandboxed applications (including apps - shipped through the Apple App Store) require the use of POSIX - shared memory (instead of System V shared memory), which adds - a number of limitations, including: + The POSIX backend can be explicitly selected using the + \c{-feature-ipc_posix} option to the Qt configure script. If it is enabled, + the \c{QT_POSIX_IPC} macro will be defined. - \list + \li Sandboxed applications on Apple platforms (including apps + shipped through the Apple App Store): This environment requires + the use of POSIX shared memory (instead of System V shared memory). + + Qt for iOS is built with support for POSIX shared memory out of the box. + However, Qt for \macos builds (including those from the Qt installer) default + to System V, making them unsuitable for App Store submission if QSharedMemory + is needed. See above for instructions to explicitly select the POSIX backend + when building Qt. + + In addition, in a sandboxed environment, the following caveats apply: + \list \li The key must be in the form \c {/}, as documented \l {https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24} {here} and \l {https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups} @@ -143,11 +155,7 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, \endlist - Qt for iOS comes with support for POSIX shared memory out of the box. - With Qt for \macos an additional configure flag must be added when - building Qt to enable the feature. To enable the feature pass - \c {-feature-ipc_posix} Note that the pre-built Qt libraries for - \macos available through the Qt installer do not include this feature. + \li Android: QSharedMemory is not supported. \endlist @@ -162,9 +170,79 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, \warning QSharedMemory changes the key in a Qt-specific way, unless otherwise specified. Interoperation with non-Qt applications is achieved by first creating a default shared memory with QSharedMemory() and then setting a native key with - setNativeKey(). When using native keys, shared memory is not protected against - multiple accesses on it (for example, unable to lock()) and a user-defined mechanism + setNativeKey(), after ensuring they use the same low-level API (System V or + POSIX). When using native keys, shared memory is not protected against multiple + accesses on it (for example, unable to lock()) and a user-defined mechanism should be used to achieve such protection. + + \section2 Alternative: Memory-Mapped File + + Another way to share memory between processes is by opening the same file + using \l QFile and mapping it into memory using QFile::map() (without + specifying the QFileDevice::MapPrivateOption option). Any writes to the + mapped segment will be observed by all other processes that have mapped the + same file. This solution has the major advantages of being independent of the + backend API and of being simpler to interoperate with from non-Qt + applications. And since \l{QTemporaryFile} is a \l{QFile}, applications can + use that class to achieve clean-up semantics and to create unique shared + memory segments too. + + To achieve locking of the shared memory segment, applications will need to + deploy their own mechanisms. This can be achieved by using \l + QBasicAtomicInteger or \c{std::atomic} in a pre-determined offset in the + segment itself. Higher-level locking primitives may be available on some + operating systems; for example, on Linux, \c{pthread_mutex_create()} can be + passed a flag to indicate that the mutex resides in a shared memory segment. + + A major drawback of using file-backed shared memory is that the operating + system will attempt to write the data to permanent storage, possibly causing + noticeable performance penalties. To avoid this, applications should locate a + RAM-backed filesystem, such as \c{tmpfs} on Linux (see + QStorageInfo::fileSystemType()), or pass a flag to the native file-opening + function to inform the OS to avoid committing the contents to storage. + + File-backed shared memory must be used with care if another process + participating is untrusted. The files may be truncated/shrunk and cause + applications accessing memory beyond the file's size to crash. + + \section3 Linux hints on memory-mapped files + + On modern Linux systems, while the \c{/tmp} directory is often a \c{tmpfs} + mount point, that is not a requirement. However, the \c{/dev/shm} directory + is required to be a \c{tmpfs} and exists for this very purpose. Do note that + it is world-readable and writable (like \c{/tmp} and \c{/var/tmp}), so one + must be careful of the contents revealed there. Another alternative is to use + the XDG Runtime Directory (see QStandardPaths::writableLocation() and + \l{QStandardPaths::RuntimeLocation}), which on Linux systems using systemd is + a user-specific \c{tmpfs}. + + An even more secure solution is to create a "memfd" using \c{memfd_create(2)} + and use interprocess communication to pass the file descriptor, like + \l{QDBusUnixFileDescriptor} or by letting the child process of a \l{QProcess} + inherit it. "memfds" can also be sealed against being shrunk, so they are + safe to be used when communicating with processes with a different privilege + level. + + \section3 FreeBSD hints on memory-mapped files + + FreeBSD also has \c{memfd_create(2)} and can pass file descriptors to other + processes using the same techniques as Linux. It does not have temporary + filesystems mounted by default. + + \section3 Windows hints on memory-mapped files + + On Windows, the application can request the operating system avoid committing + the file's contents to permanent storage. This request is performed by + passing the \c{FILE_ATTRIBUTE_TEMPORARY} flag in the \c{dwFlagsAndAttributes} + \c{CreateFile} Win32 function, the \c{_O_SHORT_LIVED} flag to \c{_open()} + low-level function, or by including the modifier "T" to the \c{fopen()} C + runtime function. + + There's also a flag to inform the operating system to delete the file when + the last handle to it is closed (\c{FILE_FLAG_DELETE_ON_CLOSE}, + \c{_O_TEMPORARY}, and the "D" modifier), but do note that all processes + attempting to open the file must agree on using this flag or not using it. A + mismatch will likely cause a sharing violation and failure to open the file. */ /*! -- cgit v1.2.3