Skip to content

Commit 6205648

Browse files
committed
fix: avoid reentrant deadlock in DSysInfo::ensureOsVersion logging
Move qWarning output out of the mutex-held region in DSysInfoPrivate::ensureOsVersion. Previously, qWarning was called while holding DSysInfoPrivate::mutex, which can deadlock if the installed Qt message handler (dtklog) re-enters DSysInfo during log formatting. Now warning text is collected into a local QString and emitted after the lock is released. Also refactor splitA_BC_DMode to accept an optional warningMessage output parameter so its diagnostic is deferred the same way, and replace the D_ASSET_EXIT macro with explicit early returns for clarity. The DDesktopEntry construction is moved before the lock so its internal warnings also stay outside the critical section. 修复 DSysInfo::ensureOsVersion 中持锁日志导致的重入死锁 将 qWarning 输出从 DSysInfoPrivate::ensureOsVersion 持锁区域移到 锁释放之后。此前在持有 DSysInfoPrivate::mutex 时调用 qWarning,若 已安装的 Qt 消息处理器(dtklog)在日志格式化过程中再次进入 DSysInfo,会导致死锁。现在告警信息先收集到局部 QString,在释放 锁后再输出。 同时重构 splitA_BC_DMode 增加可选的 warningMessage 输出参数,使 其诊断信息同样延迟输出;用显式提前返回替代 D_ASSET_EXIT 宏以 提升可读性。DDesktopEntry 构造移到加锁之前,使其内部告警也保持 在临界区之外。 Log: fix reentrant deadlock in DSysInfo::ensureOsVersion logging Change-Id: I031975ecf72528d1c2f3e48bd546e7d710641df5
1 parent 6c9c238 commit 6205648

1 file changed

Lines changed: 162 additions & 116 deletions

File tree

src/dsysinfo.cpp

Lines changed: 162 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2017 - 2023 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2017 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: LGPL-3.0-or-later
44

@@ -57,7 +57,6 @@ class Q_DECL_HIDDEN DSysInfoPrivate
5757
void ensureDeepinInfo();
5858
bool ensureOsVersion();
5959
void ensureDistributionInfo();
60-
bool splitA_BC_DMode();
6160
#endif
6261
void ensureReleaseInfo();
6362
void ensureComputerInfo();
@@ -109,6 +108,7 @@ class Q_DECL_HIDDEN DSysInfoPrivate
109108

110109
MinVersion minVersion;
111110
OSBuild osBuild;
111+
bool splitA_BC_DMode(const QString &minorVersion, MinVersion *minVersion, QString *warningMessage = nullptr);
112112
#endif
113113

114114
QScopedPointer<DDesktopEntry> distributionInfo;
@@ -141,31 +141,32 @@ void DSysInfoPrivate::ensureDistributionInfo()
141141
distributionInfo.reset(new DDesktopEntry(distributionInfoFile));
142142
}
143143

144-
bool DSysInfoPrivate::splitA_BC_DMode()
144+
bool DSysInfoPrivate::splitA_BC_DMode(const QString &minorVersion, MinVersion *minVersion, QString *warningMessage)
145145
{
146146
// A-BC-D
147147
bool ok = false;
148148
uint minv = minorVersion.toUInt(&ok);
149149
if (ok) {
150-
minVersion.D = minv % 10;
150+
minVersion->D = minv % 10;
151151
} else if (minorVersion.length() > 0) {
152152
const QString D = minorVersion.right(1);
153153
const QChar ch = D.at(0);
154154
if (ch.isDigit()) {
155-
minVersion.D = static_cast<uint>(ch.unicode() - '0');
155+
minVersion->D = static_cast<uint>(ch.unicode() - '0');
156156
} else if (ch >= 'A' && ch <= 'Z') {
157157
// 0-9...A-Z -> 10..35
158-
minVersion.D = 10 + static_cast<uint>(ch.unicode() - 'A');
158+
minVersion->D = 10 + static_cast<uint>(ch.unicode() - 'A');
159159
} else {
160-
qWarning() << "invalid minorVersion";
161-
minVersion.D = 0;
160+
if (warningMessage)
161+
*warningMessage = QStringLiteral("invalid minorVersion");
162+
minVersion->D = 0;
162163
}
163164
}
164165
uint minVer = minorVersion.left(3).toUInt();
165-
minVersion.BC = minVer % 100;
166+
minVersion->BC = minVer % 100;
166167
minVer /= 100;
167-
minVersion.A = minVer % 10;
168-
minVersion.type = MinVersion::A_BC_D;
168+
minVersion->A = minVer % 10;
169+
minVersion->type = MinVersion::A_BC_D;
169170
return ok;
170171
}
171172

@@ -251,127 +252,172 @@ void DSysInfoPrivate::ensureDeepinInfo()
251252

252253
bool DSysInfoPrivate::ensureOsVersion()
253254
{
254-
QMutexLocker locker(&mutex);
255255
#ifndef OS_VERSION_TEST_FILE // Always re-read the file when testing
256-
if (osBuild.A > 0 && !inTest())
257-
return true;
256+
{
257+
QMutexLocker locker(&mutex);
258+
if (osBuild.A > 0 && !inTest())
259+
return true;
260+
}
258261
#endif
259262

260263
DDesktopEntry entry(OS_VERSION_FILE);
264+
OSBuild parsedOsBuild;
265+
MinVersion parsedMinVersion;
266+
QString parsedMajorVersion;
267+
QString parsedMinorVersion;
268+
QString warningMessage;
261269
bool ok = false;
262270

263-
#define D_ASSET_EXIT(con, msg) do { \
264-
if (!(con)) { \
265-
qWarning() << __func__ << msg; \
266-
return false; \
267-
} \
268-
} while (false)
269-
270-
D_ASSET_EXIT(entry.status() == DDesktopEntry::NoError, entry.status());
271+
if (entry.status() != DDesktopEntry::NoError) {
272+
warningMessage = QStringLiteral("entry status: %1").arg(entry.status());
273+
} else {
274+
// 先获取版本信息
275+
// ABCDE.xyz.abc
276+
QString osb = entry.stringValue("OsBuild", "Version");
277+
QStringList osbs = osb.split(".");
278+
ok = (osbs.size() >= 2 && osbs.value(0).size() == 5);
279+
if (!ok)
280+
warningMessage = QStringLiteral("OsBuild version invalid!");
271281

272-
// 先获取版本信息
273-
// ABCDE.xyz.abc
274-
QString osb = entry.stringValue("OsBuild", "Version");
275-
QStringList osbs = osb.split(".");
276-
ok = (osbs.size() >= 2 && osbs.value(0).size() == 5);
277-
D_ASSET_EXIT(ok, "OsBuild version invalid!");
282+
if (ok) {
278283

279284
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
280-
const QStringList &left = osbs.value(0).split(QString(), Qt::SkipEmptyParts);
285+
const QStringList &left = osbs.value(0).split(QString(), Qt::SkipEmptyParts);
281286
#else
282-
const QStringList &left = osbs.value(0).split(QString(), QString::SkipEmptyParts);
287+
const QStringList &left = osbs.value(0).split(QString(), QString::SkipEmptyParts);
283288
#endif
284-
D_ASSET_EXIT(left.size() == 5, "OsBuild version(ls) invalid!");
285-
286-
int idx = 0;
287-
auto parseDigitOrAZ = [&](const QString &s, bool *outOk) -> uint {
288-
if (s.isEmpty()) { if (outOk) *outOk = false; return 0u; }
289-
const QChar ch = s.at(0);
290-
if (ch.isDigit()) { if (outOk) *outOk = true; return static_cast<uint>(ch.unicode() - '0'); }
291-
if (ch >= 'A' && ch <= 'Z') { if (outOk) *outOk = true; return static_cast<uint>(ch.toLatin1()); }
292-
if (outOk) *outOk = false;
293-
return 0u;
294-
};
289+
ok = (left.size() == 5);
290+
if (!ok)
291+
warningMessage = QStringLiteral("OsBuild version(ls) invalid!");
292+
293+
int idx = 0;
294+
auto parseDigitOrAZ = [&](const QString &s, bool *outOk) -> uint {
295+
if (s.isEmpty()) { if (outOk) *outOk = false; return 0u; }
296+
const QChar ch = s.at(0);
297+
if (ch.isDigit()) { if (outOk) *outOk = true; return static_cast<uint>(ch.unicode() - '0'); }
298+
if (ch >= 'A' && ch <= 'Z') { if (outOk) *outOk = true; return static_cast<uint>(ch.toLatin1()); }
299+
if (outOk) *outOk = false;
300+
return 0u;
301+
};
302+
303+
if (ok) {
304+
parsedOsBuild.A = parseDigitOrAZ(left.value(idx++, "0"), &ok);
305+
if (!ok)
306+
warningMessage = QStringLiteral("OsBuild version(A) invalid!");
307+
}
308+
if (ok) {
309+
parsedOsBuild.B = parseDigitOrAZ(left.value(idx++, "0"), &ok);
310+
if (!ok)
311+
warningMessage = QStringLiteral("OsBuild version(B) invalid!");
312+
}
313+
if (ok) {
314+
parsedOsBuild.C = parseDigitOrAZ(left.value(idx++, "0"), &ok);
315+
if (!ok)
316+
warningMessage = QStringLiteral("OsBuild version(C) invalid!");
317+
}
318+
if (ok) {
319+
parsedOsBuild.D = parseDigitOrAZ(left.value(idx++, "0"), &ok);
320+
if (!ok)
321+
warningMessage = QStringLiteral("OsBuild version(D) invalid!");
322+
}
323+
if (ok) {
324+
parsedOsBuild.E = parseDigitOrAZ(left.value(idx++, "0"), &ok);
325+
if (!ok)
326+
warningMessage = QStringLiteral("OsBuild version(E) invalid!");
327+
}
295328

296-
osBuild.A = parseDigitOrAZ(left.value(idx++, "0"), &ok);
297-
D_ASSET_EXIT(ok, "OsBuild version(A) invalid!");
298-
osBuild.B = parseDigitOrAZ(left.value(idx++, "0"), &ok);
299-
D_ASSET_EXIT(ok, "OsBuild version(B) invalid!");
300-
osBuild.C = parseDigitOrAZ(left.value(idx++, "0"), &ok);
301-
D_ASSET_EXIT(ok, "OsBuild version(C) invalid!");
302-
osBuild.D = parseDigitOrAZ(left.value(idx++, "0"), &ok);
303-
D_ASSET_EXIT(ok, "OsBuild version(D) invalid!");
304-
osBuild.E = parseDigitOrAZ(left.value(idx++, "0"), &ok);
305-
D_ASSET_EXIT(ok, "OsBuild version(E) invalid!");
306-
307-
// xyz
308-
osBuild.xyz = osbs.value(1).trimmed().toUInt(&ok);
309-
310-
majorVersion = entry.stringValue("MajorVersion", "Version");
311-
minorVersion = entry.stringValue("MinorVersion", "Version");
312-
313-
switch (osBuild.D) {
314-
case 7: {
315-
// Home Edition uses the form of "full version number coding -x.y.z"
316-
const QStringList &versionList = minorVersion.split('.');
317-
if (versionList.isEmpty()) {
318-
// If the reading fails, return it directly to empty
319-
qWarning() << "no minorVersion";
320-
return false;
321-
} else if (versionList.length() == 2) {
322-
// Z is 0
323-
minVersion.X = versionList.first().toUInt();
324-
minVersion.Y = versionList.last().toUInt();
325-
minVersion.Z = 0;
326-
} else if (versionList.length() == 3) {
327-
// X.Y.Z exists
328-
minVersion.X = versionList.at(0).toUInt();
329-
minVersion.Y = versionList.at(1).toUInt();
330-
minVersion.Z = versionList.at(2).toUInt();
331-
}
332-
minVersion.type = MinVersion::X_Y_Z;
333-
} break;
334-
335-
case 3: {
336-
// The community version uses the form of "full version number coding A.B.C"
337-
bool a_bc_dMode = false;
338-
const QStringList &versionList = minorVersion.split('.');
339-
if (versionList.isEmpty()) {
340-
// If the reading fails, return it directly to empty
341-
qWarning() << "no minorVersion";
342-
return false;
343-
} else if (versionList.length() == 1) {
344-
QString modeVersion = versionList.first();
345-
if (modeVersion.length() == 2) {
346-
//A.B.C mode and B c are 0
347-
minVersion.A = modeVersion.toUInt();
348-
minVersion.B = 0;
349-
minVersion.C = 0;
350-
} else {
351-
// A_BC_D mode
352-
splitA_BC_DMode();
353-
a_bc_dMode = true;
329+
if (ok) {
330+
// xyz
331+
parsedOsBuild.xyz = osbs.value(1).trimmed().toUInt(&ok);
332+
}
333+
334+
if (ok) {
335+
parsedMajorVersion = entry.stringValue("MajorVersion", "Version");
336+
parsedMinorVersion = entry.stringValue("MinorVersion", "Version");
337+
338+
switch (parsedOsBuild.D) {
339+
case 7: {
340+
// Home Edition uses the form of "full version number coding -x.y.z"
341+
const QStringList &versionList = parsedMinorVersion.split('.');
342+
if (versionList.isEmpty()) {
343+
// If the reading fails, return it directly to empty
344+
warningMessage = QStringLiteral("no minorVersion");
345+
ok = false;
346+
} else if (versionList.length() == 2) {
347+
// Z is 0
348+
parsedMinVersion.X = versionList.first().toUInt();
349+
parsedMinVersion.Y = versionList.last().toUInt();
350+
parsedMinVersion.Z = 0;
351+
} else if (versionList.length() == 3) {
352+
// X.Y.Z exists
353+
parsedMinVersion.X = versionList.at(0).toUInt();
354+
parsedMinVersion.Y = versionList.at(1).toUInt();
355+
parsedMinVersion.Z = versionList.at(2).toUInt();
356+
}
357+
parsedMinVersion.type = MinVersion::X_Y_Z;
358+
} break;
359+
360+
case 3: {
361+
// The community version uses the form of "full version number coding A.B.C"
362+
bool a_bc_dMode = false;
363+
const QStringList &versionList = parsedMinorVersion.split('.');
364+
if (versionList.isEmpty()) {
365+
// If the reading fails, return it directly to empty
366+
warningMessage = QStringLiteral("no minorVersion");
367+
ok = false;
368+
} else if (versionList.length() == 1) {
369+
QString modeVersion = versionList.first();
370+
if (modeVersion.length() == 2) {
371+
//A.B.C mode and B c are 0
372+
parsedMinVersion.A = modeVersion.toUInt();
373+
parsedMinVersion.B = 0;
374+
parsedMinVersion.C = 0;
375+
} else {
376+
// A_BC_D mode
377+
splitA_BC_DMode(parsedMinorVersion, &parsedMinVersion, &warningMessage);
378+
a_bc_dMode = true;
379+
}
380+
} else if (versionList.length() == 2) {
381+
// C=0
382+
parsedMinVersion.A = versionList.first().toUInt();
383+
parsedMinVersion.B = versionList.last().toUInt();
384+
parsedMinVersion.C = 0;
385+
} else if (versionList.length() == 3) {
386+
// A.B.C exists
387+
parsedMinVersion.A = versionList.at(0).toUInt();
388+
parsedMinVersion.B = versionList.at(1).toUInt();
389+
parsedMinVersion.C = versionList.at(2).toUInt();
390+
}
391+
392+
if (!a_bc_dMode)
393+
parsedMinVersion.type = MinVersion::A_B_C;
394+
} break;
395+
default: {
396+
// A-BC-D
397+
splitA_BC_DMode(parsedMinorVersion, &parsedMinVersion, &warningMessage);
398+
} break;
399+
}
354400
}
355-
} else if (versionList.length() == 2) {
356-
// C=0
357-
minVersion.A = versionList.first().toUInt();
358-
minVersion.B = versionList.last().toUInt();
359-
minVersion.C = 0;
360-
} else if (versionList.length() == 3) {
361-
// A.B.C exists
362-
minVersion.A = versionList.at(0).toUInt();
363-
minVersion.B = versionList.at(1).toUInt();
364-
minVersion.C = versionList.at(2).toUInt();
365401
}
366402

367-
if (!a_bc_dMode)
368-
minVersion.type = MinVersion::A_B_C;
369-
} break;
370-
default: {
371-
// A-BC-D
372-
ok = splitA_BC_DMode();
373-
} break;
403+
{
404+
QMutexLocker locker(&mutex);
405+
#ifndef OS_VERSION_TEST_FILE // Always re-read the file when testing
406+
if (osBuild.A > 0 && !inTest())
407+
return true;
408+
#endif
409+
if (ok) {
410+
osBuild = parsedOsBuild;
411+
minVersion = parsedMinVersion;
412+
majorVersion = parsedMajorVersion;
413+
minorVersion = parsedMinorVersion;
414+
}
415+
}
374416
}
417+
418+
if (!warningMessage.isEmpty())
419+
qWarning() << "ensureOsVersion" << warningMessage;
420+
375421
return ok;
376422
}
377423

0 commit comments

Comments
 (0)