Warning, file /plasma/drkonqi/src/linuxprocmapsparser.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org> 0004 */ 0005 0006 #include "linuxprocmapsparser.h" 0007 0008 #include <QByteArrayList> 0009 #include <QDebug> 0010 #include <QFile> 0011 #include <QRegularExpression> 0012 0013 #include <errno.h> 0014 #include <string.h> 0015 #include <sys/stat.h> 0016 #include <sys/types.h> 0017 #include <unistd.h> 0018 0019 #include "drkonqi_debug.h" 0020 0021 bool LinuxProc::isLibraryPath(const QString &path) 0022 { 0023 // This intentionally matches potential suffixes, i.e. "/usr/lib/foo.so.0" but also "foo.so (deleted)" 0024 static QRegularExpression soExpression(QStringLiteral("(?<path>.+\\.(so|py)([^/]*))$")); 0025 const auto soMatch = soExpression.match(path); 0026 return soMatch.isValid() && soMatch.hasMatch() && !soMatch.captured(u"path").isEmpty(); 0027 } 0028 0029 bool LinuxProc::hasMapsDeletedFiles(const QString &exePathString, const QByteArray &maps, Check check) 0030 { 0031 const QByteArray exePath = QFile::encodeName(exePathString); 0032 const QByteArrayList lines = maps.split('\n'); 0033 for (const auto &line : lines) { 0034 if (line.isEmpty()) { 0035 continue; 0036 } 0037 // Walk string by tokens. This is by far the easiest way to parse the format as anything after 0038 // the first 5 fields (minus the tokens) is the pathname. The pathname may be nothing, or contain more 0039 // spaces in turn. Qt has no convenient API for this, use strtok. 0040 0041 QByteArray mutableLine = line; 0042 // address 0043 strtok(mutableLine.data(), " "); 0044 // perms 0045 strtok(nullptr, " "); 0046 // offset 0047 strtok(nullptr, " "); 0048 // dev 0049 strtok(nullptr, " "); 0050 // inode 0051 const QByteArray inode(strtok(nullptr, " ")); 0052 // remainder is the pathname 0053 const QByteArray pathname = QByteArray(strtok(nullptr, "\n")).simplified(); // simplify to make evaluation easier 0054 0055 if (pathname.isEmpty() || pathname.at(0) != QLatin1Char('/')) { 0056 // Could be pseudo entry like [heap] or anonymous region. 0057 continue; 0058 } 0059 0060 if (pathname.startsWith(QByteArrayLiteral("/memfd"))) { 0061 // Qml.so's JIT shows up under memfd. This is a false positive as it isn't a real path in the 0062 // file system. Skip over it. 0063 continue; 0064 } 0065 0066 const QByteArray deletedMarker = QByteArrayLiteral(" (deleted)"); 0067 // We filter only .so files to ensure that we don't trip over cache files or the like. 0068 // NB: includes .so* and .py* since we also implicitly support snakes to 0069 // a degree 0070 // As a result we need to explicitly look for the main executable. 0071 if (pathname == exePath + deletedMarker) { 0072 return true; 0073 } 0074 0075 if (pathname != exePath && !isLibraryPath(QFile::decodeName(pathname))) { 0076 continue; // not exe and not a library. 0077 } 0078 0079 // Deleted marker always declares something missing. Even when we perform additional stat checks on it. 0080 if (pathname.endsWith(deletedMarker)) { 0081 return true; 0082 } 0083 0084 switch (check) { 0085 case Check::DeletedMarker: { 0086 // If we get here the file hasn't been marked deleted. 0087 break; 0088 } 0089 case Check::Stat: { 0090 struct stat info { 0091 }; 0092 const int ret = stat(pathname.constData(), &info); 0093 if (ret == -1) { 0094 qCWarning(DRKONQI_LOG) << "Couldn't stat file, assuming it was deleted" << pathname << strerror(errno); 0095 return true; 0096 break; 0097 } 0098 0099 if (info.st_ino != inode.toULongLong()) { 0100 qCWarning(DRKONQI_LOG) << "Found mismatching inode on" << pathname << info.st_ino << inode; 0101 return true; 0102 break; 0103 } 0104 0105 // It's very awkward but st_dev seems dodgy at least with btrfs. The dev_t the kernel has is not the one 0106 // stat has and what's more the kernel has one that solid doesn't know about either. That may simply be 0107 // because btrfs makes up fake dev_ts since multiple btrfs subvolumes may be on the same block device. 0108 // Anyway, it's unfortunate but I guess we had best not look at the device. 0109 } break; 0110 } 0111 } 0112 0113 return false; 0114 }