1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2022 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6#include "qsystemsemaphore.h"
7#include "qsystemsemaphore_p.h"
8
9#include <qdebug.h>
10#include <qfile.h>
11#include <qcoreapplication.h>
12
13#if QT_CONFIG(sysv_sem)
14
15#include <sys/types.h>
16#include <sys/ipc.h>
17#include <sys/sem.h>
18#include <fcntl.h>
19#include <errno.h>
20
21#if defined(Q_OS_DARWIN)
22#include "private/qcore_mac_p.h"
23#endif
24
25#include "private/qcore_unix_p.h"
26
27// OpenBSD 4.2 doesn't define EIDRM, see BUGS section:
28// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2
29#if defined(Q_OS_OPENBSD) && !defined(EIDRM)
30#define EIDRM EINVAL
31#endif
32
33QT_BEGIN_NAMESPACE
34
35using namespace Qt::StringLiterals;
36
37bool QSystemSemaphoreSystemV::runtimeSupportCheck()
38{
39#if defined(Q_OS_DARWIN)
40 if (qt_apple_isSandboxed())
41 return false;
42#endif
43 static const bool result = []() {
44 (void)semget(IPC_PRIVATE, nsems: -1, semflg: 0); // this will fail
45 return errno != ENOSYS;
46 }();
47 return result;
48}
49
50/*!
51 \internal
52
53 Setup unix_key
54 */
55key_t QSystemSemaphoreSystemV::handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode)
56{
57 if (unix_key != -1)
58 return unix_key; // we already have a semaphore
59
60#if defined(Q_OS_DARWIN)
61 if (qt_apple_isSandboxed()) {
62 // attempting to use System V semaphores will get us a SIGSYS
63 self->setError(QSystemSemaphore::PermissionDenied,
64 QSystemSemaphore::tr("%1: System V semaphores are not available for "
65 "sandboxed applications. Please build Qt with "
66 "-feature-ipc_posix")
67 .arg("QSystemSemaphore::handle:"_L1));
68 return -1;
69 }
70#endif
71
72 nativeKeyFile = QFile::encodeName(fileName: self->nativeKey.nativeKey());
73 if (nativeKeyFile.isEmpty()) {
74 self->setError(e: QSystemSemaphore::KeyError,
75 message: QSystemSemaphore::tr(sourceText: "%1: key is empty")
76 .arg(a: "QSystemSemaphore::handle:"_L1));
77 return -1;
78 }
79
80 // ftok requires that an actual file exists somewhere
81 int built = QtIpcCommon::createUnixKeyFile(fileName: nativeKeyFile);
82 if (-1 == built) {
83 self->setError(e: QSystemSemaphore::KeyError,
84 message: QSystemSemaphore::tr(sourceText: "%1: unable to make key")
85 .arg(a: "QSystemSemaphore::handle:"_L1));
86
87 return -1;
88 }
89 createdFile = (1 == built);
90
91 // Get the unix key for the created file
92 unix_key = ftok(pathname: nativeKeyFile, proj_id: int(self->nativeKey.type()));
93 if (-1 == unix_key) {
94 self->setError(e: QSystemSemaphore::KeyError,
95 message: QSystemSemaphore::tr(sourceText: "%1: ftok failed")
96 .arg(a: "QSystemSemaphore::handle:"_L1));
97 return -1;
98 }
99
100 // Get semaphore
101 semaphore = semget(key: unix_key, nsems: 1, semflg: 0600 | IPC_CREAT | IPC_EXCL);
102 if (-1 == semaphore) {
103 if (errno == EEXIST)
104 semaphore = semget(key: unix_key, nsems: 1, semflg: 0600 | IPC_CREAT);
105 if (-1 == semaphore) {
106 self->setUnixErrorString("QSystemSemaphore::handle"_L1);
107 cleanHandle(self);
108 return -1;
109 }
110 } else {
111 createdSemaphore = true;
112 // Force cleanup of file, it is possible that it can be left over from a crash
113 createdFile = true;
114 }
115
116 if (mode == QSystemSemaphore::Create) {
117 createdSemaphore = true;
118 createdFile = true;
119 }
120
121 // Created semaphore so initialize its value.
122 if (createdSemaphore && self->initialValue >= 0) {
123 qt_semun init_op;
124 init_op.val = self->initialValue;
125 if (-1 == semctl(semid: semaphore, semnum: 0, SETVAL, init_op)) {
126 self->setUnixErrorString("QSystemSemaphore::handle"_L1);
127 cleanHandle(self);
128 return -1;
129 }
130 }
131
132 return unix_key;
133}
134
135/*!
136 \internal
137
138 Cleanup the unix_key
139 */
140void QSystemSemaphoreSystemV::cleanHandle(QSystemSemaphorePrivate *self)
141{
142 unix_key = -1;
143
144 // remove the file if we made it
145 if (createdFile) {
146 unlink(name: nativeKeyFile.constData());
147 createdFile = false;
148 }
149
150 if (createdSemaphore) {
151 if (-1 != semaphore) {
152 if (-1 == semctl(semid: semaphore, semnum: 0, IPC_RMID, 0)) {
153 self->setUnixErrorString("QSystemSemaphore::cleanHandle"_L1);
154#if defined QSYSTEMSEMAPHORE_DEBUG
155 qDebug("QSystemSemaphoreSystemV::cleanHandle semctl failed.");
156#endif
157 }
158 semaphore = -1;
159 }
160 createdSemaphore = false;
161 }
162}
163
164/*!
165 \internal
166 */
167bool QSystemSemaphoreSystemV::modifySemaphore(QSystemSemaphorePrivate *self, int count)
168{
169 if (handle(self, mode: QSystemSemaphore::Open) == -1)
170 return false;
171
172 struct sembuf operation;
173 operation.sem_num = 0;
174 operation.sem_op = count;
175 operation.sem_flg = SEM_UNDO;
176
177 int res;
178 QT_EINTR_LOOP(res, semop(semaphore, &operation, 1));
179 if (-1 == res) {
180 // If the semaphore was removed be nice and create it and then modifySemaphore again
181 if (errno == EINVAL || errno == EIDRM) {
182 semaphore = -1;
183 cleanHandle(self);
184 handle(self, mode: QSystemSemaphore::Open);
185 return modifySemaphore(self, count);
186 }
187 self->setUnixErrorString("QSystemSemaphore::modifySemaphore"_L1);
188#if defined QSYSTEMSEMAPHORE_DEBUG
189 qDebug("QSystemSemaphoreSystemV::modify failed %d %d %d %d %d",
190 count, int(semctl(semaphore, 0, GETVAL)), int(errno), int(EIDRM), int(EINVAL);
191#endif
192 return false;
193 }
194
195 self->clearError();
196 return true;
197}
198
199
200QT_END_NAMESPACE
201
202#endif // QT_CONFIG(sysv_sem)
203

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