diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /vcl/source/helper/driverblocklist.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/source/helper/driverblocklist.cxx')
-rw-r--r-- | vcl/source/helper/driverblocklist.cxx | 768 |
1 files changed, 768 insertions, 0 deletions
diff --git a/vcl/source/helper/driverblocklist.cxx b/vcl/source/helper/driverblocklist.cxx new file mode 100644 index 000000000..855376dc1 --- /dev/null +++ b/vcl/source/helper/driverblocklist.cxx @@ -0,0 +1,768 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <driverblocklist.hxx> + +#include <algorithm> +#include <string_view> + +#include <sal/log.hxx> + +#ifdef _WIN32 +#if !defined WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#endif + +namespace DriverBlocklist +{ +static OperatingSystem getOperatingSystem(std::string_view rString) +{ + if (rString == "all") + return DRIVER_OS_ALL; + else if (rString == "7") + return DRIVER_OS_WINDOWS_7; + else if (rString == "8") + return DRIVER_OS_WINDOWS_8; + else if (rString == "8_1") + return DRIVER_OS_WINDOWS_8_1; + else if (rString == "10") + return DRIVER_OS_WINDOWS_10; + else if (rString == "windows") + return DRIVER_OS_WINDOWS_ALL; + else if (rString == "linux") + return DRIVER_OS_LINUX; + else if (rString == "osx_10_5") + return DRIVER_OS_OSX_10_5; + else if (rString == "osx_10_6") + return DRIVER_OS_OSX_10_6; + else if (rString == "osx_10_7") + return DRIVER_OS_OSX_10_7; + else if (rString == "osx_10_8") + return DRIVER_OS_OSX_10_8; + else if (rString == "osx") + return DRIVER_OS_OSX_ALL; + else if (rString == "android") + return DRIVER_OS_ANDROID; + return DRIVER_OS_UNKNOWN; +} + +static VersionComparisonOp getComparison(std::string_view rString) +{ + if (rString == "less") + { + return DRIVER_LESS_THAN; + } + else if (rString == "less_equal") + { + return DRIVER_LESS_THAN_OR_EQUAL; + } + else if (rString == "greater") + { + return DRIVER_GREATER_THAN; + } + else if (rString == "greater_equal") + { + return DRIVER_GREATER_THAN_OR_EQUAL; + } + else if (rString == "equal") + { + return DRIVER_EQUAL; + } + else if (rString == "not_equal") + { + return DRIVER_NOT_EQUAL; + } + else if (rString == "between_exclusive") + { + return DRIVER_BETWEEN_EXCLUSIVE; + } + else if (rString == "between_inclusive") + { + return DRIVER_BETWEEN_INCLUSIVE; + } + else if (rString == "between_inclusive_start") + { + return DRIVER_BETWEEN_INCLUSIVE_START; + } + + throw InvalidFileException(); +} + +static OUString GetVendorId(std::string_view rString) +{ + if (rString == "all") + { + return ""; + } + else if (rString == "intel") + { + return "0x8086"; + } + else if (rString == "nvidia") + { + return "0x10de"; + } + else if (rString == "amd") + { + return "0x1002"; + } + else if (rString == "microsoft") + { + return "0x1414"; + } + else + { + // Allow having simply the hex number as such there, too. + return OStringToOUString(rString, RTL_TEXTENCODING_UTF8); + } +} + +OUString GetVendorId(DeviceVendor id) +{ + assert(id >= 0 && id < DeviceVendorMax); + + switch (id) + { + case VendorAll: + return ""; + case VendorIntel: + return "0x8086"; + case VendorNVIDIA: + return "0x10de"; + case VendorAMD: + return "0x1002"; + case VendorMicrosoft: + return "0x1414"; + } + abort(); +} + +DeviceVendor GetVendorFromId(uint32_t id) +{ + switch (id) + { + case 0x8086: + return VendorIntel; + case 0x10de: + return VendorNVIDIA; + case 0x1002: + return VendorAMD; + case 0x1414: + return VendorMicrosoft; + default: + return VendorAll; + } +} + +std::string_view GetVendorNameFromId(uint32_t id) +{ + switch (id) + { + case 0x8086: + return "Intel"; + case 0x10de: + return "Nvidia"; + case 0x1002: + return "AMD"; + case 0x1414: + return "Microsoft"; + default: + return "?"; + } +} + +Parser::Parser(const OUString& rURL, std::vector<DriverInfo>& rDriverList, VersionType versionType) + : meBlockType(BlockType::UNKNOWN) + , mrDriverList(rDriverList) + , maURL(rURL) + , mVersionType(versionType) +{ +} + +bool Parser::parse() +{ + try + { + xmlreader::XmlReader aReader(maURL); + handleContent(aReader); + } + catch (...) + { + mrDriverList.clear(); + return false; + } + return true; +} + +// This allows us to pad driver version 'substrings' with 0s, this +// effectively allows us to treat the version numbers as 'decimals'. This is +// a little strange but this method seems to do the right thing for all +// different vendor's driver strings. i.e. .98 will become 9800, which is +// larger than .978 which would become 9780. +static void PadDriverDecimal(char* aString) +{ + for (int i = 0; i < 4; i++) + { + if (!aString[i]) + { + for (int c = i; c < 4; c++) + { + aString[c] = '0'; + } + break; + } + } + aString[4] = 0; +} + +// All destination string storage needs to have at least 5 bytes available. +static bool SplitDriverVersion(const char* aSource, char* aAStr, char* aBStr, char* aCStr, + char* aDStr, VersionType versionType) +{ + // sscanf doesn't do what we want here to we parse this manually. + int len = strlen(aSource); + char* dest[4] = { aAStr, aBStr, aCStr, aDStr }; + unsigned destIdx = 0; + unsigned destPos = 0; + + for (int i = 0; i < len; i++) + { + if (destIdx >= SAL_N_ELEMENTS(dest)) + { + // Invalid format found. Ensure we don't access dest beyond bounds. + return false; + } + + if (aSource[i] == '.') + { + dest[destIdx++][destPos] = 0; + destPos = 0; + continue; + } + + if (destPos > 3) + { + // Ignore more than 4 chars. Ensure we never access dest[destIdx] + // beyond its bounds. + continue; + } + + dest[destIdx][destPos++] = aSource[i]; + } + + // Add last terminator. + dest[destIdx][destPos] = 0; + + // Vulkan version numbers have only 3 fields. + if (versionType == VersionType::Vulkan && destIdx == SAL_N_ELEMENTS(dest) - 2) + dest[++destIdx][0] = '\0'; + if (destIdx != SAL_N_ELEMENTS(dest) - 1) + { + return false; + } + return true; +} + +static bool ParseDriverVersion(std::u16string_view aVersion, uint64_t& rNumericVersion, + VersionType versionType) +{ + rNumericVersion = 0; + + int a, b, c, d; + char aStr[8], bStr[8], cStr[8], dStr[8]; + /* honestly, why do I even bother */ + OString aOVersion = OUStringToOString(aVersion, RTL_TEXTENCODING_UTF8); + if (!SplitDriverVersion(aOVersion.getStr(), aStr, bStr, cStr, dStr, versionType)) + return false; + + if (versionType == VersionType::OpenGL) + { + PadDriverDecimal(bStr); + PadDriverDecimal(cStr); + PadDriverDecimal(dStr); + } + + a = atoi(aStr); + b = atoi(bStr); + c = atoi(cStr); + d = atoi(dStr); + + if (versionType == VersionType::Vulkan) + assert(d == 0); + + if (a < 0 || a > 0xffff) + return false; + if (b < 0 || b > 0xffff) + return false; + if (c < 0 || c > 0xffff) + return false; + if (d < 0 || d > 0xffff) + return false; + + rNumericVersion = GFX_DRIVER_VERSION(a, b, c, d); + return true; +} + +uint64_t Parser::getVersion(std::string_view rString) +{ + OUString aString = OStringToOUString(rString, RTL_TEXTENCODING_UTF8); + uint64_t nVersion; + bool bResult = ParseDriverVersion(aString, nVersion, mVersionType); + + if (!bResult) + { + throw InvalidFileException(); + } + + return nVersion; +} + +void Parser::handleDevices(DriverInfo& rDriver, xmlreader::XmlReader& rReader) +{ + int nLevel = 1; + bool bInMsg = false; + + while (true) + { + xmlreader::Span name; + int nsId; + + xmlreader::XmlReader::Result res + = rReader.nextItem(xmlreader::XmlReader::Text::Normalized, &name, &nsId); + + if (res == xmlreader::XmlReader::Result::Begin) + { + ++nLevel; + if (nLevel > 2) + throw InvalidFileException(); + + if (name == "msg") + { + bInMsg = true; + } + else if (name == "device") + { + int nsIdDeveice; + while (rReader.nextAttribute(&nsIdDeveice, &name)) + { + if (name == "id") + { + name = rReader.getAttributeValue(false); + OString aDeviceId(name.begin, name.length); + rDriver.maDevices.push_back( + OStringToOUString(aDeviceId, RTL_TEXTENCODING_UTF8)); + } + } + } + else + throw InvalidFileException(); + } + else if (res == xmlreader::XmlReader::Result::End) + { + --nLevel; + bInMsg = false; + if (!nLevel) + break; + } + else if (res == xmlreader::XmlReader::Result::Text) + { + if (bInMsg) + { + OString sMsg(name.begin, name.length); + rDriver.maMsg = OStringToOUString(sMsg, RTL_TEXTENCODING_UTF8); + } + } + } +} + +void Parser::handleEntry(DriverInfo& rDriver, xmlreader::XmlReader& rReader) +{ + if (meBlockType == BlockType::ALLOWLIST) + { + rDriver.mbAllowlisted = true; + } + else if (meBlockType == BlockType::DENYLIST) + { + rDriver.mbAllowlisted = false; + } + else if (meBlockType == BlockType::UNKNOWN) + { + throw InvalidFileException(); + } + + xmlreader::Span name; + int nsId; + + while (rReader.nextAttribute(&nsId, &name)) + { + if (name == "os") + { + name = rReader.getAttributeValue(false); + OString sOS(name.begin, name.length); + rDriver.meOperatingSystem = getOperatingSystem(sOS); + } + else if (name == "vendor") + { + name = rReader.getAttributeValue(false); + OString sVendor(name.begin, name.length); + rDriver.maAdapterVendor = GetVendorId(sVendor); + } + else if (name == "compare") + { + name = rReader.getAttributeValue(false); + OString sCompare(name.begin, name.length); + rDriver.meComparisonOp = getComparison(sCompare); + } + else if (name == "version") + { + name = rReader.getAttributeValue(false); + OString sVersion(name.begin, name.length); + rDriver.mnDriverVersion = getVersion(sVersion); + } + else if (name == "minVersion") + { + name = rReader.getAttributeValue(false); + OString sMinVersion(name.begin, name.length); + rDriver.mnDriverVersion = getVersion(sMinVersion); + } + else if (name == "maxVersion") + { + name = rReader.getAttributeValue(false); + OString sMaxVersion(name.begin, name.length); + rDriver.mnDriverVersionMax = getVersion(sMaxVersion); + } + else + { + OString aAttrName(name.begin, name.length); + SAL_WARN("vcl.driver", "unsupported attribute: " << aAttrName); + } + } + + handleDevices(rDriver, rReader); +} + +void Parser::handleList(xmlreader::XmlReader& rReader) +{ + xmlreader::Span name; + int nsId; + + while (true) + { + xmlreader::XmlReader::Result res + = rReader.nextItem(xmlreader::XmlReader::Text::NONE, &name, &nsId); + + if (res == xmlreader::XmlReader::Result::Begin) + { + if (name == "entry") + { + DriverInfo aDriver; + handleEntry(aDriver, rReader); + mrDriverList.push_back(aDriver); + } + else if (name == "entryRange") + { + DriverInfo aDriver; + handleEntry(aDriver, rReader); + mrDriverList.push_back(aDriver); + } + else + { + throw InvalidFileException(); + } + } + else if (res == xmlreader::XmlReader::Result::End) + { + break; + } + } +} + +void Parser::handleContent(xmlreader::XmlReader& rReader) +{ + while (true) + { + xmlreader::Span name; + int nsId; + + xmlreader::XmlReader::Result res + = rReader.nextItem(xmlreader::XmlReader::Text::NONE, &name, &nsId); + + if (res == xmlreader::XmlReader::Result::Begin) + { + if (name == "allowlist") + { + meBlockType = BlockType::ALLOWLIST; + handleList(rReader); + } + else if (name == "denylist") + { + meBlockType = BlockType::DENYLIST; + handleList(rReader); + } + else if (name == "root") + { + } + else + { + throw InvalidFileException(); + } + } + else if (res == xmlreader::XmlReader::Result::End) + { + if (name == "allowlist" || name == "denylist") + { + meBlockType = BlockType::UNKNOWN; + } + } + else if (res == xmlreader::XmlReader::Result::Done) + { + break; + } + } +} + +static OperatingSystem getOperatingSystem() +{ +#ifdef _WIN32 + // OS version in 16.16 major/minor form + // based on http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx + switch (DriverBlocklist::GetWindowsVersion()) + { + case 0x00060001: + return DRIVER_OS_WINDOWS_7; + case 0x00060002: + return DRIVER_OS_WINDOWS_8; + case 0x00060003: + return DRIVER_OS_WINDOWS_8_1; + case 0x000A0000: // Major 10 Minor 0 + return DRIVER_OS_WINDOWS_10; + default: + return DRIVER_OS_UNKNOWN; + } +#elif defined LINUX + return DRIVER_OS_LINUX; +#else + return DRIVER_OS_UNKNOWN; +#endif +} + +namespace +{ +struct compareIgnoreAsciiCase +{ + explicit compareIgnoreAsciiCase(const OUString& rString) + : maString(rString) + { + } + + bool operator()(std::u16string_view rCompare) + { + return maString.equalsIgnoreAsciiCase(rCompare); + } + +private: + OUString maString; +}; +} + +const uint64_t allDriverVersions = ~(uint64_t(0)); + +DriverInfo::DriverInfo() + : meOperatingSystem(DRIVER_OS_UNKNOWN) + , maAdapterVendor(GetVendorId(VendorAll)) + , mbAllowlisted(false) + , meComparisonOp(DRIVER_COMPARISON_IGNORED) + , mnDriverVersion(0) + , mnDriverVersionMax(0) +{ +} + +DriverInfo::DriverInfo(OperatingSystem os, const OUString& vendor, VersionComparisonOp op, + uint64_t driverVersion, bool bAllowlisted, + const char* suggestedVersion /* = nullptr */) + : meOperatingSystem(os) + , maAdapterVendor(vendor) + , mbAllowlisted(bAllowlisted) + , meComparisonOp(op) + , mnDriverVersion(driverVersion) + , mnDriverVersionMax(0) +{ + if (suggestedVersion) + maSuggestedVersion + = OStringToOUString(std::string_view(suggestedVersion), RTL_TEXTENCODING_UTF8); +} + +bool FindBlocklistedDeviceInList(std::vector<DriverInfo>& aDeviceInfos, VersionType versionType, + std::u16string_view sDriverVersion, + std::u16string_view sAdapterVendorID, + OUString const& sAdapterDeviceID, OperatingSystem system, + const OUString& blocklistURL) +{ + uint64_t driverVersion; + ParseDriverVersion(sDriverVersion, driverVersion, versionType); + + bool match = false; + for (std::vector<DriverInfo>::size_type i = 0; i < aDeviceInfos.size(); i++) + { + bool osMatch = false; + if (aDeviceInfos[i].meOperatingSystem == DRIVER_OS_ALL) + osMatch = true; + else if (aDeviceInfos[i].meOperatingSystem == system) + osMatch = true; + else if (aDeviceInfos[i].meOperatingSystem == DRIVER_OS_WINDOWS_ALL + && system >= DRIVER_OS_WINDOWS_FIRST && system <= DRIVER_OS_WINDOWS_LAST) + osMatch = true; + else if (aDeviceInfos[i].meOperatingSystem == DRIVER_OS_OSX_ALL + && system >= DRIVER_OS_OSX_FIRST && system <= DRIVER_OS_OSX_LAST) + osMatch = true; + if (!osMatch) + { + continue; + } + + if (!aDeviceInfos[i].maAdapterVendor.equalsIgnoreAsciiCase(GetVendorId(VendorAll)) + && !aDeviceInfos[i].maAdapterVendor.equalsIgnoreAsciiCase(sAdapterVendorID)) + { + continue; + } + + if (std::none_of(aDeviceInfos[i].maDevices.begin(), aDeviceInfos[i].maDevices.end(), + compareIgnoreAsciiCase("all")) + && std::none_of(aDeviceInfos[i].maDevices.begin(), aDeviceInfos[i].maDevices.end(), + compareIgnoreAsciiCase(sAdapterDeviceID))) + { + continue; + } + + switch (aDeviceInfos[i].meComparisonOp) + { + case DRIVER_LESS_THAN: + match = driverVersion < aDeviceInfos[i].mnDriverVersion; + break; + case DRIVER_LESS_THAN_OR_EQUAL: + match = driverVersion <= aDeviceInfos[i].mnDriverVersion; + break; + case DRIVER_GREATER_THAN: + match = driverVersion > aDeviceInfos[i].mnDriverVersion; + break; + case DRIVER_GREATER_THAN_OR_EQUAL: + match = driverVersion >= aDeviceInfos[i].mnDriverVersion; + break; + case DRIVER_EQUAL: + match = driverVersion == aDeviceInfos[i].mnDriverVersion; + break; + case DRIVER_NOT_EQUAL: + match = driverVersion != aDeviceInfos[i].mnDriverVersion; + break; + case DRIVER_BETWEEN_EXCLUSIVE: + match = driverVersion > aDeviceInfos[i].mnDriverVersion + && driverVersion < aDeviceInfos[i].mnDriverVersionMax; + break; + case DRIVER_BETWEEN_INCLUSIVE: + match = driverVersion >= aDeviceInfos[i].mnDriverVersion + && driverVersion <= aDeviceInfos[i].mnDriverVersionMax; + break; + case DRIVER_BETWEEN_INCLUSIVE_START: + match = driverVersion >= aDeviceInfos[i].mnDriverVersion + && driverVersion < aDeviceInfos[i].mnDriverVersionMax; + break; + case DRIVER_COMPARISON_IGNORED: + // We don't have a comparison op, so we match everything. + match = true; + break; + default: + SAL_WARN("vcl.driver", "Bogus op in " << blocklistURL); + break; + } + + if (match || aDeviceInfos[i].mnDriverVersion == allDriverVersions) + { + // white listed drivers + if (aDeviceInfos[i].mbAllowlisted) + { + SAL_INFO("vcl.driver", "allowlisted driver"); + return false; + } + + match = true; + if (!aDeviceInfos[i].maSuggestedVersion.isEmpty()) + { + SAL_WARN("vcl.driver", "use : " << aDeviceInfos[i].maSuggestedVersion); + } + break; + } + } + + SAL_INFO("vcl.driver", (match ? "denylisted" : "not denylisted") << " in " << blocklistURL); + return match; +} + +bool IsDeviceBlocked(const OUString& blocklistURL, VersionType versionType, + std::u16string_view driverVersion, std::u16string_view vendorId, + const OUString& deviceId) +{ + std::vector<DriverInfo> driverList; + Parser parser(blocklistURL, driverList, versionType); + if (!parser.parse()) + { + SAL_WARN("vcl.driver", "error parsing denylist " << blocklistURL); + return false; + } + return FindBlocklistedDeviceInList(driverList, versionType, driverVersion, vendorId, deviceId, + getOperatingSystem(), blocklistURL); +} + +#ifdef _WIN32 +int32_t GetWindowsVersion() +{ + static int32_t winVersion = []() { + // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are + // subject to manifest-based behavior since Windows 8.1, so give wrong results. + // Another approach would be to use NetWkstaGetInfo, but that has some small + // reported delays (some milliseconds), and might get slower in domains with + // poor network connections. + // So go with a solution described at https://msdn.microsoft.com/en-us/library/ms724429 + HINSTANCE hLibrary = LoadLibraryW(L"kernel32.dll"); + if (hLibrary != nullptr) + { + wchar_t szPath[MAX_PATH]; + DWORD dwCount = GetModuleFileNameW(hLibrary, szPath, SAL_N_ELEMENTS(szPath)); + FreeLibrary(hLibrary); + if (dwCount != 0 && dwCount < SAL_N_ELEMENTS(szPath)) + { + dwCount = GetFileVersionInfoSizeW(szPath, nullptr); + if (dwCount != 0) + { + std::unique_ptr<char[]> ver(new char[dwCount]); + if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE) + { + void* pBlock = nullptr; + UINT dwBlockSz = 0; + if (VerQueryValueW(ver.get(), L"\\", &pBlock, &dwBlockSz) != FALSE + && dwBlockSz >= sizeof(VS_FIXEDFILEINFO)) + { + VS_FIXEDFILEINFO* vinfo = static_cast<VS_FIXEDFILEINFO*>(pBlock); + return int32_t(vinfo->dwProductVersionMS); + } + } + } + } + } + return 0; + }(); + + return winVersion; +} +#endif + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |