Skip to content

Commit 39fa460

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 39fa460

1 file changed

Lines changed: 165 additions & 117 deletions

File tree

src/dsysinfo.cpp

Lines changed: 165 additions & 117 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();
@@ -95,7 +94,7 @@ class Q_DECL_HIDDEN DSysInfoPrivate
9594
Type type;
9695
};
9796
struct OSBuild {
98-
OSBuild():A(0), B(0), C(0), D(0), xyz(100){
97+
OSBuild():A(0), B(0), C(0), D(0), E(0), xyz(100){
9998
}
10099

101100
/* ABCDE.xyz模式
@@ -109,6 +108,8 @@ class Q_DECL_HIDDEN DSysInfoPrivate
109108

110109
MinVersion minVersion;
111110
OSBuild osBuild;
111+
bool osVersionParsed = false;
112+
bool splitA_BC_DMode(const QString &minorVersion, MinVersion *minVersion, QString *warningMessage = nullptr);
112113
#endif
113114

114115
QScopedPointer<DDesktopEntry> distributionInfo;
@@ -141,31 +142,32 @@ void DSysInfoPrivate::ensureDistributionInfo()
141142
distributionInfo.reset(new DDesktopEntry(distributionInfoFile));
142143
}
143144

144-
bool DSysInfoPrivate::splitA_BC_DMode()
145+
bool DSysInfoPrivate::splitA_BC_DMode(const QString &minorVersion, MinVersion *minVersion, QString *warningMessage)
145146
{
146147
// A-BC-D
147148
bool ok = false;
148149
uint minv = minorVersion.toUInt(&ok);
149150
if (ok) {
150-
minVersion.D = minv % 10;
151+
minVersion->D = minv % 10;
151152
} else if (minorVersion.length() > 0) {
152153
const QString D = minorVersion.right(1);
153154
const QChar ch = D.at(0);
154155
if (ch.isDigit()) {
155-
minVersion.D = static_cast<uint>(ch.unicode() - '0');
156+
minVersion->D = static_cast<uint>(ch.unicode() - '0');
156157
} else if (ch >= 'A' && ch <= 'Z') {
157158
// 0-9...A-Z -> 10..35
158-
minVersion.D = 10 + static_cast<uint>(ch.unicode() - 'A');
159+
minVersion->D = 10 + static_cast<uint>(ch.unicode() - 'A');
159160
} else {
160-
qWarning() << "invalid minorVersion";
161-
minVersion.D = 0;
161+
if (warningMessage)
162+
*warningMessage = QStringLiteral("invalid minorVersion");
163+
minVersion->D = 0;
162164
}
163165
}
164166
uint minVer = minorVersion.left(3).toUInt();
165-
minVersion.BC = minVer % 100;
167+
minVersion->BC = minVer % 100;
166168
minVer /= 100;
167-
minVersion.A = minVer % 10;
168-
minVersion.type = MinVersion::A_BC_D;
169+
minVersion->A = minVer % 10;
170+
minVersion->type = MinVersion::A_BC_D;
169171
return ok;
170172
}
171173

@@ -251,127 +253,173 @@ void DSysInfoPrivate::ensureDeepinInfo()
251253

252254
bool DSysInfoPrivate::ensureOsVersion()
253255
{
254-
QMutexLocker locker(&mutex);
255256
#ifndef OS_VERSION_TEST_FILE // Always re-read the file when testing
256-
if (osBuild.A > 0 && !inTest())
257-
return true;
257+
{
258+
QMutexLocker locker(&mutex);
259+
if (osVersionParsed && !inTest())
260+
return true;
261+
}
258262
#endif
259263

260264
DDesktopEntry entry(OS_VERSION_FILE);
265+
OSBuild parsedOsBuild;
266+
MinVersion parsedMinVersion;
267+
QString parsedMajorVersion;
268+
QString parsedMinorVersion;
269+
QString warningMessage;
261270
bool ok = false;
262271

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());
272+
if (entry.status() != DDesktopEntry::NoError) {
273+
warningMessage = QStringLiteral("entry status: %1").arg(entry.status());
274+
} else {
275+
// 先获取版本信息
276+
// ABCDE.xyz.abc
277+
QString osb = entry.stringValue("OsBuild", "Version");
278+
QStringList osbs = osb.split(".");
279+
ok = (osbs.size() >= 2 && osbs.value(0).size() == 5);
280+
if (!ok)
281+
warningMessage = QStringLiteral("OsBuild version invalid!");
271282

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!");
283+
if (ok) {
278284

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

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;
330+
if (ok) {
331+
// xyz
332+
parsedOsBuild.xyz = osbs.value(1).trimmed().toUInt(&ok);
333+
}
334+
335+
if (ok) {
336+
parsedMajorVersion = entry.stringValue("MajorVersion", "Version");
337+
parsedMinorVersion = entry.stringValue("MinorVersion", "Version");
338+
339+
switch (parsedOsBuild.D) {
340+
case 7: {
341+
// Home Edition uses the form of "full version number coding -x.y.z"
342+
const QStringList &versionList = parsedMinorVersion.split('.');
343+
if (versionList.isEmpty()) {
344+
// If the reading fails, return it directly to empty
345+
warningMessage = QStringLiteral("no minorVersion");
346+
ok = false;
347+
} else if (versionList.length() == 2) {
348+
// Z is 0
349+
parsedMinVersion.X = versionList.first().toUInt();
350+
parsedMinVersion.Y = versionList.last().toUInt();
351+
parsedMinVersion.Z = 0;
352+
} else if (versionList.length() == 3) {
353+
// X.Y.Z exists
354+
parsedMinVersion.X = versionList.at(0).toUInt();
355+
parsedMinVersion.Y = versionList.at(1).toUInt();
356+
parsedMinVersion.Z = versionList.at(2).toUInt();
357+
}
358+
parsedMinVersion.type = MinVersion::X_Y_Z;
359+
} break;
360+
361+
case 3: {
362+
// The community version uses the form of "full version number coding A.B.C"
363+
bool a_bc_dMode = false;
364+
const QStringList &versionList = parsedMinorVersion.split('.');
365+
if (versionList.isEmpty()) {
366+
// If the reading fails, return it directly to empty
367+
warningMessage = QStringLiteral("no minorVersion");
368+
ok = false;
369+
} else if (versionList.length() == 1) {
370+
QString modeVersion = versionList.first();
371+
if (modeVersion.length() == 2) {
372+
//A.B.C mode and B c are 0
373+
parsedMinVersion.A = modeVersion.toUInt();
374+
parsedMinVersion.B = 0;
375+
parsedMinVersion.C = 0;
376+
} else {
377+
// A_BC_D mode
378+
splitA_BC_DMode(parsedMinorVersion, &parsedMinVersion, &warningMessage);
379+
a_bc_dMode = true;
380+
}
381+
} else if (versionList.length() == 2) {
382+
// C=0
383+
parsedMinVersion.A = versionList.first().toUInt();
384+
parsedMinVersion.B = versionList.last().toUInt();
385+
parsedMinVersion.C = 0;
386+
} else if (versionList.length() == 3) {
387+
// A.B.C exists
388+
parsedMinVersion.A = versionList.at(0).toUInt();
389+
parsedMinVersion.B = versionList.at(1).toUInt();
390+
parsedMinVersion.C = versionList.at(2).toUInt();
391+
}
392+
393+
if (!a_bc_dMode)
394+
parsedMinVersion.type = MinVersion::A_B_C;
395+
} break;
396+
default: {
397+
// A-BC-D
398+
splitA_BC_DMode(parsedMinorVersion, &parsedMinVersion, &warningMessage);
399+
} break;
400+
}
354401
}
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();
365402
}
366403

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;
404+
{
405+
QMutexLocker locker(&mutex);
406+
#ifndef OS_VERSION_TEST_FILE // Always re-read the file when testing
407+
if (osVersionParsed && !inTest())
408+
return true;
409+
#endif
410+
if (ok) {
411+
osBuild = parsedOsBuild;
412+
minVersion = parsedMinVersion;
413+
majorVersion = parsedMajorVersion;
414+
minorVersion = parsedMinorVersion;
415+
}
416+
osVersionParsed = true;
417+
}
374418
}
419+
420+
if (!warningMessage.isEmpty())
421+
qWarning() << "ensureOsVersion" << warningMessage;
422+
375423
return ok;
376424
}
377425

0 commit comments

Comments
 (0)