Avoid potential data races caused by qt_ntfs_permission_lookup

qt_ntfs_permission_lookup is a global, non-atomic variable which
could cause problems in case of multiple threads. Introduce a
new atomic variable to handle permission lookups but instead of
manual incrementation/decrementation, implement a class to manage
the variable.

Since the atomic variable is not directly available to the user,
implement helper functions to increase/decrease/check the status
of the variable.

Task-number: QTBUG-105804
Change-Id: If6cbcdd653c7f50ad9853a5c309e24fdeb520788
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
Mate Barany 2023-01-13 18:18:08 +01:00
parent 214c3a033a
commit 84f0596c0c
8 changed files with 124 additions and 22 deletions

View File

@ -11,3 +11,18 @@ qt_ntfs_permission_lookup++; // turn checking on
qt_ntfs_permission_lookup--; // turn it off again qt_ntfs_permission_lookup--; // turn it off again
//! [1] //! [1]
//! [raii]
void complexFunction()
{
QNtfsPermissionCheckGuard permissionGuard; // check is enabled
// do complex things here that need permission check enabled
} // as the guard goes out of scope the check is disabled
//! [raii]
//! [free-funcs]
qAreNtfsPermissionChecksEnabled(); // check status
qEnableNtfsPermissionChecks(); // turn checking on
qDisableNtfsPermissionChecks(); // turn it off again
//! [free-funcs]

View File

@ -26,6 +26,27 @@ namespace std {
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
#ifdef Q_OS_WIN
Q_CORE_EXPORT bool qEnableNtfsPermissionChecks() noexcept;
Q_CORE_EXPORT bool qDisableNtfsPermissionChecks() noexcept;
Q_CORE_EXPORT bool qAreNtfsPermissionChecksEnabled() noexcept;
class [[nodiscard]] QNtfsPermissionCheckGuard
{
Q_DISABLE_COPY_MOVE(QNtfsPermissionCheckGuard)
public:
QNtfsPermissionCheckGuard()
{
qEnableNtfsPermissionChecks();
}
~QNtfsPermissionCheckGuard()
{
qDisableNtfsPermissionChecks();
}
};
#endif // Q_OS_WIN
#if QT_CONFIG(cxx17_filesystem) #if QT_CONFIG(cxx17_filesystem)
namespace QtPrivate { namespace QtPrivate {
inline QString fromFilesystemPath(const std::filesystem::path &path) inline QString fromFilesystemPath(const std::filesystem::path &path)

View File

@ -114,6 +114,20 @@ void QFileDevicePrivate::setError(QFileDevice::FileError err, int errNum)
to increment or decrement \c qt_ntfs_permission_lookup before any to increment or decrement \c qt_ntfs_permission_lookup before any
threads other than the main thread have started or after every thread threads other than the main thread have started or after every thread
other than the main thread has ended. other than the main thread has ended.
\note From Qt 6.6 the variable \c qt_ntfs_permission_lookup is
deprecated. Please use the following alternatives.
The safe and easy way to manage permission checks is to use the RAII class
\c QNtfsPermissionCheckGuard.
\snippet ntfsp.cpp raii
If you need more fine-grained control, it is possible to manage the permission
with the following functions instead:
\snippet ntfsp.cpp free-funcs
*/ */
//************* QFileDevice //************* QFileDevice

View File

@ -278,6 +278,19 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request)
threads other than the main thread have started or after every thread threads other than the main thread have started or after every thread
other than the main thread has ended. other than the main thread has ended.
\note From Qt 6.6 the variable \c qt_ntfs_permission_lookup is
deprecated. Please use the following alternatives.
The safe and easy way to manage permission checks is to use the RAII class
\c QNtfsPermissionCheckGuard.
\snippet ntfsp.cpp raii
If you need more fine-grained control, it is possible to manage the permission
with the following functions instead:
\snippet ntfsp.cpp free-funcs
\section1 Performance Considerations \section1 Performance Considerations
Some of QFileInfo's functions query the file system, but for Some of QFileInfo's functions query the file system, but for

View File

@ -384,6 +384,46 @@ constexpr QFileDevice::Permissions toSpecificPermissions(PermissionTag tag,
Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0; Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0;
static QBasicAtomicInt qt_ntfs_permission_lookup_v2 = Q_BASIC_ATOMIC_INITIALIZER(0);
/*!
\internal
Returns true if the check was previously enabled.
*/
bool qEnableNtfsPermissionChecks() noexcept
{
return qt_ntfs_permission_lookup_v2.fetchAndAddRelaxed(1)
+ qt_ntfs_permission_lookup
!= 0;
}
/*!
\internal
Returns true if the check is disabled, i.e. there are no more users.
*/
bool qDisableNtfsPermissionChecks() noexcept
{
return qt_ntfs_permission_lookup_v2.fetchAndSubRelaxed(1)
+ qt_ntfs_permission_lookup
== 1;
}
/*!
\internal
Returns true if the check is enabled.
*/
bool qAreNtfsPermissionChecksEnabled() noexcept
{
return qt_ntfs_permission_lookup_v2.loadRelaxed()
+ qt_ntfs_permission_lookup;
}
/*! /*!
\class QNativeFilePermissions \class QNativeFilePermissions
\internal \internal
@ -1078,8 +1118,7 @@ QString QFileSystemEngine::owner(const QFileSystemEntry &entry, QAbstractFileEng
{ {
QString name; QString name;
#if QT_CONFIG(fslibs) #if QT_CONFIG(fslibs)
extern int qt_ntfs_permission_lookup; if (qAreNtfsPermissionChecksEnabled()) {
if (qt_ntfs_permission_lookup > 0) {
initGlobalSid(); initGlobalSid();
{ {
PSID pOwner = 0; PSID pOwner = 0;
@ -1133,7 +1172,7 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
QFileSystemMetaData::MetaDataFlags what) QFileSystemMetaData::MetaDataFlags what)
{ {
#if QT_CONFIG(fslibs) #if QT_CONFIG(fslibs)
if (qt_ntfs_permission_lookup > 0) { if (qAreNtfsPermissionChecksEnabled()) {
initGlobalSid(); initGlobalSid();
QString fname = entry.nativeFilePath(); QString fname = entry.nativeFilePath();

View File

@ -12,7 +12,6 @@
#include <qdebug.h> #include <qdebug.h>
#include <qdir.h> #include <qdir.h>
#include <qfileinfo.h> #include <qfileinfo.h>
#include <qscopedvaluerollback.h>
#include <qstringlist.h> #include <qstringlist.h>
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
@ -40,7 +39,6 @@
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#define DRIVE "Q:" #define DRIVE "Q:"
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
#else #else
#define DRIVE #define DRIVE
#endif #endif
@ -453,8 +451,7 @@ void tst_QDir::mkdirWithPermissions()
QFETCH(QFile::Permissions, permissions); QFETCH(QFile::Permissions, permissions);
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
QScopedValueRollback<int> ntfsMode(qt_ntfs_permission_lookup); QNtfsPermissionCheckGuard permissionGuard;
++qt_ntfs_permission_lookup;
#endif #endif
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
auto restoreMask = qScopeGuard([oldMask = umask(0)] { umask(oldMask); }); auto restoreMask = qScopeGuard([oldMask = umask(0)] { umask(oldMask); });

View File

@ -171,6 +171,7 @@ private slots:
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
void permissionsNtfs_data(); void permissionsNtfs_data();
void permissionsNtfs(); void permissionsNtfs();
void deprecatedNtfsPermissionCheck();
#endif #endif
void setPermissions_data(); void setPermissions_data();
void setPermissions(); void setPermissions();
@ -1267,8 +1268,7 @@ void tst_QFile::createFilePermissions()
QFETCH(QFile::Permissions, permissions); QFETCH(QFile::Permissions, permissions);
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
QScopedValueRollback<int> ntfsMode(qt_ntfs_permission_lookup); QNtfsPermissionCheckGuard permissionGuard;
++qt_ntfs_permission_lookup;
#endif #endif
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
auto restoreMask = qScopeGuard([oldMask = umask(0)] { umask(oldMask); }); auto restoreMask = qScopeGuard([oldMask = umask(0)] { umask(oldMask); });
@ -1392,7 +1392,7 @@ void tst_QFile::permissions()
} }
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
if (qt_ntfs_permission_lookup) if (qAreNtfsPermissionChecksEnabled())
QEXPECT_FAIL("readonly", "QTBUG-25630", Abort); QEXPECT_FAIL("readonly", "QTBUG-25630", Abort);
#endif #endif
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
@ -1414,10 +1414,21 @@ void tst_QFile::permissionsNtfs_data()
void tst_QFile::permissionsNtfs() void tst_QFile::permissionsNtfs()
{ {
QScopedValueRollback<int> ntfsMode(qt_ntfs_permission_lookup); QNtfsPermissionCheckGuard permissionGuard;
qt_ntfs_permission_lookup++;
permissions(); permissions();
} }
void tst_QFile::deprecatedNtfsPermissionCheck()
{
QScopedValueRollback<int> guard(qt_ntfs_permission_lookup);
QCOMPARE(qAreNtfsPermissionChecksEnabled(), false);
qt_ntfs_permission_lookup++;
QCOMPARE(qAreNtfsPermissionChecksEnabled(), true);
qt_ntfs_permission_lookup--;
QCOMPARE(qAreNtfsPermissionChecksEnabled(), false);
}
#endif #endif
void tst_QFile::setPermissions_data() void tst_QFile::setPermissions_data()

View File

@ -4,7 +4,6 @@
#include <QTest> #include <QTest>
#include <QStandardPaths> #include <QStandardPaths>
#include <QScopeGuard> #include <QScopeGuard>
#include <QScopedValueRollback>
#include <qfile.h> #include <qfile.h>
#include <qdir.h> #include <qdir.h>
@ -43,9 +42,6 @@
#endif #endif
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
QT_BEGIN_NAMESPACE
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
QT_END_NAMESPACE
bool IsUserAdmin(); bool IsUserAdmin();
#endif #endif
@ -1905,8 +1901,7 @@ void tst_QFileInfo::isWritable()
#endif #endif
#if defined (Q_OS_WIN) #if defined (Q_OS_WIN)
QScopedValueRollback<int> ntfsMode(qt_ntfs_permission_lookup); QNtfsPermissionCheckGuard permissionGuard;
qt_ntfs_permission_lookup = 1;
QFileInfo fi2(QFile::decodeName(qgetenv("SystemRoot") + "/system.ini")); QFileInfo fi2(QFile::decodeName(qgetenv("SystemRoot") + "/system.ini"));
QVERIFY(fi2.exists()); QVERIFY(fi2.exists());
QCOMPARE(fi2.isWritable(), IsUserAdmin()); QCOMPARE(fi2.isWritable(), IsUserAdmin());
@ -2156,7 +2151,7 @@ void tst_QFileInfo::owner()
NetApiBufferFree(pBuf); NetApiBufferFree(pBuf);
} }
} }
qt_ntfs_permission_lookup = 1; QNtfsPermissionCheckGuard permissionGuard;
#endif #endif
if (userName.isEmpty()) if (userName.isEmpty())
QSKIP("Can't retrieve the user name"); QSKIP("Can't retrieve the user name");
@ -2173,9 +2168,6 @@ void tst_QFileInfo::owner()
QCOMPARE(fi.owner(), userName); QCOMPARE(fi.owner(), userName);
QFile::remove(fileName); QFile::remove(fileName);
#if defined(Q_OS_WIN)
qt_ntfs_permission_lookup = 0;
#endif
} }
void tst_QFileInfo::group() void tst_QFileInfo::group()