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
5#include "qsharedmemory.h"
6#include "qsharedmemory_p.h"
7
8#include "qtipccommon_p.h"
9
10#include <qdir.h>
11#include <qdebug.h>
12
13#include <errno.h>
14
15#if QT_CONFIG(sharedmemory)
16#if QT_CONFIG(sysv_shm)
17#include <sys/types.h>
18#include <sys/ipc.h>
19#include <sys/mman.h>
20#include <sys/shm.h>
21#include <sys/stat.h>
22#include <fcntl.h>
23#include <unistd.h>
24
25#include "private/qcore_unix_p.h"
26#if defined(Q_OS_DARWIN)
27#include "private/qcore_mac_p.h"
28#endif
29
30QT_BEGIN_NAMESPACE
31
32using namespace Qt::StringLiterals;
33using namespace QtIpcCommon;
34
35bool QSharedMemorySystemV::runtimeSupportCheck()
36{
37#if defined(Q_OS_DARWIN)
38 if (qt_apple_isSandboxed())
39 return false;
40#endif
41 static const bool result = []() {
42 (void)shmget(IPC_PRIVATE, size: ~size_t(0), shmflg: 0); // this will fail
43 return errno != ENOSYS;
44 }();
45 return result;
46}
47
48
49inline void QSharedMemorySystemV::updateNativeKeyFile(const QNativeIpcKey &nativeKey)
50{
51 Q_ASSERT(nativeKeyFile.isEmpty() );
52 if (!nativeKey.nativeKey().isEmpty())
53 nativeKeyFile = QFile::encodeName(fileName: nativeKey.nativeKey());
54}
55
56/*!
57 \internal
58
59 If not already made create the handle used for accessing the shared memory.
60*/
61key_t QSharedMemorySystemV::handle(QSharedMemoryPrivate *self)
62{
63 // already made
64 if (unix_key)
65 return unix_key;
66
67 // don't allow making handles on empty keys
68 if (nativeKeyFile.isEmpty())
69 updateNativeKeyFile(nativeKey: self->nativeKey);
70 if (nativeKeyFile.isEmpty()) {
71 self->setError(e: QSharedMemory::KeyError,
72 message: QSharedMemory::tr(s: "%1: key is empty")
73 .arg(a: "QSharedMemory::handle:"_L1));
74 return 0;
75 }
76
77 unix_key = ftok(pathname: nativeKeyFile, proj_id: int(self->nativeKey.type()));
78 if (unix_key < 0) {
79 self->setUnixErrorString("QSharedMemory::handle"_L1);
80 nativeKeyFile.clear();
81 unix_key = 0;
82 }
83 return unix_key;
84}
85
86bool QSharedMemorySystemV::cleanHandle(QSharedMemoryPrivate *self)
87{
88 if (unix_key == 0)
89 return true;
90
91 // Get the number of current attachments
92 struct shmid_ds shmid_ds;
93 QByteArray keyfile = std::exchange(obj&: nativeKeyFile, new_val: QByteArray());
94
95 int id = shmget(key: unix_key, size: 0, shmflg: 0400);
96 unix_key = 0;
97 if (shmctl(shmid: id, IPC_STAT, buf: &shmid_ds))
98 return errno != EINVAL;
99
100 // If there are still attachments, keep the keep file and shm
101 if (shmid_ds.shm_nattch != 0)
102 return true;
103
104 if (shmctl(shmid: id, IPC_RMID, buf: &shmid_ds) < 0) {
105 if (errno != EINVAL) {
106 self->setUnixErrorString("QSharedMemory::remove"_L1);
107 return false;
108 }
109 };
110
111 // remove file
112 return unlink(name: keyfile) == 0;
113}
114
115bool QSharedMemorySystemV::create(QSharedMemoryPrivate *self, qsizetype size)
116{
117 // build file if needed
118 bool createdFile = false;
119 updateNativeKeyFile(nativeKey: self->nativeKey);
120 int built = createUnixKeyFile(fileName: nativeKeyFile);
121 if (built == -1) {
122 self->setError(e: QSharedMemory::KeyError,
123 message: QSharedMemory::tr(s: "%1: unable to make key")
124 .arg(a: "QSharedMemory::handle:"_L1));
125 return false;
126 }
127 if (built == 1) {
128 createdFile = true;
129 }
130
131 // get handle
132 if (!handle(self)) {
133 if (createdFile)
134 unlink(name: nativeKeyFile);
135 return false;
136 }
137
138 // create
139 if (-1 == shmget(key: unix_key, size: size_t(size), shmflg: 0600 | IPC_CREAT | IPC_EXCL)) {
140 const auto function = "QSharedMemory::create"_L1;
141 switch (errno) {
142 case EINVAL:
143 self->setError(e: QSharedMemory::InvalidSize,
144 message: QSharedMemory::tr(s: "%1: system-imposed size restrictions")
145 .arg(a: "QSharedMemory::handle"_L1));
146 break;
147 default:
148 self->setUnixErrorString(function);
149 }
150 if (createdFile && self->error != QSharedMemory::AlreadyExists)
151 unlink(name: nativeKeyFile);
152 return false;
153 }
154
155 return true;
156}
157
158bool QSharedMemorySystemV::attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode)
159{
160 // grab the shared memory segment id
161 int id = shmget(key: unix_key, size: 0, shmflg: (mode == QSharedMemory::ReadOnly ? 0400 : 0600));
162 if (-1 == id) {
163 self->setUnixErrorString("QSharedMemory::attach (shmget)"_L1);
164 unix_key = 0;
165 nativeKeyFile.clear();
166 return false;
167 }
168
169 // grab the memory
170 self->memory = shmat(shmid: id, shmaddr: nullptr, shmflg: (mode == QSharedMemory::ReadOnly ? SHM_RDONLY : 0));
171 if (self->memory == MAP_FAILED) {
172 self->memory = nullptr;
173 self->setUnixErrorString("QSharedMemory::attach (shmat)"_L1);
174 return false;
175 }
176
177 // grab the size
178 shmid_ds shmid_ds;
179 if (!shmctl(shmid: id, IPC_STAT, buf: &shmid_ds)) {
180 self->size = (qsizetype)shmid_ds.shm_segsz;
181 } else {
182 self->setUnixErrorString("QSharedMemory::attach (shmctl)"_L1);
183 return false;
184 }
185
186 return true;
187}
188
189bool QSharedMemorySystemV::detach(QSharedMemoryPrivate *self)
190{
191 // detach from the memory segment
192 if (shmdt(shmaddr: self->memory) < 0) {
193 const auto function = "QSharedMemory::detach"_L1;
194 switch (errno) {
195 case EINVAL:
196 self->setError(e: QSharedMemory::NotFound,
197 message: QSharedMemory::tr(s: "%1: not attached").arg(a: function));
198 break;
199 default:
200 self->setUnixErrorString(function);
201 }
202 return false;
203 }
204 self->memory = nullptr;
205 self->size = 0;
206
207 return cleanHandle(self);
208}
209
210QT_END_NAMESPACE
211
212#endif // QT_CONFIG(sysv_shm)
213#endif // QT_CONFIG(sharedmemory)
214

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