1// Copyright (C) 2015 Konstantin Ritt <ritt.ks@gmail.com>
2// Copyright (C) 2016 The Qt Company Ltd.
3// Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias Koenig <tobias.koenig@kdab.com>
4// Copyright (C) 2022 Intel Corporation.
5// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
6// Qt-Security score:significant reason:default
7
8#include "qsystemsemaphore.h"
9#include "qsystemsemaphore_p.h"
10
11#include <qdebug.h>
12#include <qfile.h>
13#include <qcoreapplication.h>
14
15#if QT_CONFIG(posix_sem)
16
17#include <sys/types.h>
18#include <fcntl.h>
19#include <errno.h>
20
21#ifdef Q_OS_UNIX
22# include "private/qcore_unix_p.h"
23#else
24# define QT_EINTR_LOOP_VAL(var, val, cmd) \
25 (void)var; var = cmd
26# define QT_EINTR_LOOP(var, cmd) QT_EINTR_LOOP_VAL(var, -1, cmd)
27#endif
28
29// OpenBSD 4.2 doesn't define EIDRM, see BUGS section:
30// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2
31#if defined(Q_OS_OPENBSD) && !defined(EIDRM)
32#define EIDRM EINVAL
33#endif
34
35QT_BEGIN_NAMESPACE
36
37using namespace Qt::StringLiterals;
38
39bool QSystemSemaphorePosix::runtimeSupportCheck()
40{
41 static const bool result = []() {
42 sem_open(name: "/", oflag: 0, 0, 0); // this WILL fail
43 return errno != ENOSYS;
44 }();
45 return result;
46}
47
48bool QSystemSemaphorePosix::handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode)
49{
50 if (semaphore != SEM_FAILED)
51 return true; // we already have a semaphore
52
53 const QByteArray semName = QFile::encodeName(fileName: self->nativeKey.nativeKey());
54 if (semName.isEmpty()) {
55 self->setError(e: QSystemSemaphore::KeyError,
56 message: QSystemSemaphore::tr(sourceText: "%1: key is empty")
57 .arg(a: "QSystemSemaphore::handle"_L1));
58 return false;
59 }
60
61 // Always try with O_EXCL so we know whether we created the semaphore.
62 int oflag = O_CREAT | O_EXCL;
63 for (int tryNum = 0, maxTries = 1; tryNum < maxTries; ++tryNum) {
64 do {
65 semaphore = ::sem_open(name: semName.constData(), oflag: oflag, 0600, self->initialValue);
66 } while (semaphore == SEM_FAILED && errno == EINTR);
67 if (semaphore == SEM_FAILED && errno == EEXIST) {
68 if (mode == QSystemSemaphore::Create) {
69 if (::sem_unlink(name: semName.constData()) == -1 && errno != ENOENT) {
70 self->setUnixErrorString("QSystemSemaphore::handle (sem_unlink)"_L1);
71 return false;
72 }
73 // Race condition: the semaphore might be recreated before
74 // we call sem_open again, so we'll retry several times.
75 maxTries = 3;
76 } else {
77 // Race condition: if it no longer exists at the next sem_open
78 // call, we won't realize we created it, so we'll leak it later.
79 oflag &= ~O_EXCL;
80 maxTries = 2;
81 }
82 } else {
83 break;
84 }
85 }
86
87 if (semaphore == SEM_FAILED) {
88 self->setUnixErrorString("QSystemSemaphore::handle"_L1);
89 return false;
90 }
91
92 createdSemaphore = (oflag & O_EXCL) != 0;
93
94 return true;
95}
96
97void QSystemSemaphorePosix::cleanHandle(QSystemSemaphorePrivate *self)
98{
99 if (semaphore != SEM_FAILED) {
100 if (::sem_close(sem: semaphore) == -1) {
101 self->setUnixErrorString("QSystemSemaphore::cleanHandle (sem_close)"_L1);
102#if defined QSYSTEMSEMAPHORE_DEBUG
103 qDebug("QSystemSemaphore::cleanHandle sem_close failed.");
104#endif
105 }
106 semaphore = SEM_FAILED;
107 }
108
109 if (createdSemaphore) {
110 const QByteArray semName = QFile::encodeName(fileName: self->nativeKey.nativeKey());
111 if (::sem_unlink(name: semName) == -1 && errno != ENOENT) {
112 self->setUnixErrorString("QSystemSemaphore::cleanHandle (sem_unlink)"_L1);
113#if defined QSYSTEMSEMAPHORE_DEBUG
114 qDebug("QSystemSemaphorePosix::cleanHandle sem_unlink failed.");
115#endif
116 }
117 createdSemaphore = false;
118 }
119}
120
121bool QSystemSemaphorePosix::modifySemaphore(QSystemSemaphorePrivate *self, int count)
122{
123 if (!handle(self, mode: QSystemSemaphore::Open))
124 return false;
125
126 if (count > 0) {
127 int cnt = count;
128 do {
129 if (::sem_post(sem: semaphore) == -1) {
130#if defined(Q_OS_VXWORKS)
131 if (errno == EINVAL) {
132 semaphore = SEM_FAILED;
133 return modifySemaphore(self, cnt);
134 }
135#endif
136 self->setUnixErrorString("QSystemSemaphore::modifySemaphore (sem_post)"_L1);
137#if defined QSYSTEMSEMAPHORE_DEBUG
138 qDebug("QSystemSemaphorePosix::modify sem_post failed %d %d", count, errno);
139#endif
140 // rollback changes to preserve the SysV semaphore behavior
141 for ( ; cnt < count; ++cnt) {
142 int res;
143 QT_EINTR_LOOP(res, ::sem_wait(semaphore));
144 }
145 return false;
146 }
147 --cnt;
148 } while (cnt > 0);
149 } else {
150 int res;
151 QT_EINTR_LOOP(res, ::sem_wait(semaphore));
152 if (res == -1) {
153 // If the semaphore was removed be nice and create it and then modifySemaphore again
154 if (errno == EINVAL || errno == EIDRM) {
155 semaphore = SEM_FAILED;
156 return modifySemaphore(self, count);
157 }
158 self->setUnixErrorString("QSystemSemaphore::modifySemaphore (sem_wait)"_L1);
159#if defined QSYSTEMSEMAPHORE_DEBUG
160 qDebug("QSystemSemaphorePosix::modify sem_wait failed %d %d", count, errno);
161#endif
162 return false;
163 }
164 }
165
166 self->clearError();
167 return true;
168}
169
170QT_END_NAMESPACE
171
172#endif // QT_CONFIG(posix_sem)
173

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