LibreOffice Module vcl (master)  1
driverblocklist.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <driverblocklist.hxx>
11 
12 #include <algorithm>
13 
14 #include <sal/log.hxx>
15 
16 #ifdef _WIN32
17 #if !defined WIN32_LEAN_AND_MEAN
18 #define WIN32_LEAN_AND_MEAN
19 #endif
20 #include <windows.h>
21 #endif
22 
23 namespace DriverBlocklist
24 {
25 static OperatingSystem getOperatingSystem(const OString& rString)
26 {
27  if (rString == "all")
28  return DRIVER_OS_ALL;
29  else if (rString == "7")
30  return DRIVER_OS_WINDOWS_7;
31  else if (rString == "8")
32  return DRIVER_OS_WINDOWS_8;
33  else if (rString == "8_1")
34  return DRIVER_OS_WINDOWS_8_1;
35  else if (rString == "10")
36  return DRIVER_OS_WINDOWS_10;
37  else if (rString == "linux")
38  return DRIVER_OS_LINUX;
39  else if (rString == "osx_10_5")
40  return DRIVER_OS_OSX_10_5;
41  else if (rString == "osx_10_6")
42  return DRIVER_OS_OSX_10_6;
43  else if (rString == "osx_10_7")
44  return DRIVER_OS_OSX_10_7;
45  else if (rString == "osx_10_8")
46  return DRIVER_OS_OSX_10_8;
47  else if (rString == "android")
48  return DRIVER_OS_ANDROID;
49  return DRIVER_OS_UNKNOWN;
50 }
51 
52 static VersionComparisonOp getComparison(const OString& rString)
53 {
54  if (rString == "less")
55  {
56  return DRIVER_LESS_THAN;
57  }
58  else if (rString == "less_equal")
59  {
61  }
62  else if (rString == "greater")
63  {
64  return DRIVER_GREATER_THAN;
65  }
66  else if (rString == "greater_equal")
67  {
69  }
70  else if (rString == "equal")
71  {
72  return DRIVER_EQUAL;
73  }
74  else if (rString == "not_equal")
75  {
76  return DRIVER_NOT_EQUAL;
77  }
78  else if (rString == "between_exclusive")
79  {
81  }
82  else if (rString == "between_inclusive")
83  {
85  }
86  else if (rString == "between_inclusive_start")
87  {
89  }
90 
91  throw InvalidFileException();
92 }
93 
94 static OUString GetVendorId(const OString& rString)
95 {
96  if (rString == "all")
97  {
98  return "";
99  }
100  else if (rString == "intel")
101  {
102  return "0x8086";
103  }
104  else if (rString == "nvidia")
105  {
106  return "0x10de";
107  }
108  else if (rString == "amd")
109  {
110  return "0x1002";
111  }
112  else if (rString == "microsoft")
113  {
114  return "0x1414";
115  }
116  else
117  {
118  // Allow having simply the hex number as such there, too.
119  return OStringToOUString(rString, RTL_TEXTENCODING_UTF8);
120  }
121 }
122 
124 {
125  assert(id >= 0 && id < DeviceVendorMax);
126 
127  switch (id)
128  {
129  case VendorAll:
130  return "";
131  case VendorIntel:
132  return "0x8086";
133  case VendorNVIDIA:
134  return "0x10de";
135  case VendorAMD:
136  return "0x1002";
137  case VendorMicrosoft:
138  return "0x1414";
139  }
140  abort();
141 }
142 
144 {
145  switch (id)
146  {
147  case 0x8086:
148  return VendorIntel;
149  case 0x10de:
150  return VendorNVIDIA;
151  case 0x1002:
152  return VendorAMD;
153  case 0x1414:
154  return VendorMicrosoft;
155  default:
156  return VendorAll;
157  }
158 }
159 
160 OUStringLiteral GetVendorNameFromId(uint32_t id)
161 {
162  switch (id)
163  {
164  case 0x8086:
165  return "Intel";
166  case 0x10de:
167  return "Nvidia";
168  case 0x1002:
169  return "AMD";
170  case 0x1414:
171  return "Microsoft";
172  default:
173  return "?";
174  }
175 }
176 
177 Parser::Parser(const OUString& rURL, std::vector<DriverInfo>& rDriverList)
178  : meBlockType(BlockType::UNKNOWN)
179  , mrDriverList(rDriverList)
180  , maURL(rURL)
181 {
182 }
183 
185 {
186  try
187  {
188  xmlreader::XmlReader aReader(maURL);
189  handleContent(aReader);
190  }
191  catch (...)
192  {
193  mrDriverList.clear();
194  return false;
195  }
196  return true;
197 }
198 
199 // This allows us to pad driver version 'substrings' with 0s, this
200 // effectively allows us to treat the version numbers as 'decimals'. This is
201 // a little strange but this method seems to do the right thing for all
202 // different vendor's driver strings. i.e. .98 will become 9800, which is
203 // larger than .978 which would become 9780.
204 static void PadDriverDecimal(char* aString)
205 {
206  for (int i = 0; i < 4; i++)
207  {
208  if (!aString[i])
209  {
210  for (int c = i; c < 4; c++)
211  {
212  aString[c] = '0';
213  }
214  break;
215  }
216  }
217  aString[4] = 0;
218 }
219 
220 // All destination string storage needs to have at least 5 bytes available.
221 static bool SplitDriverVersion(const char* aSource, char* aAStr, char* aBStr, char* aCStr,
222  char* aDStr)
223 {
224  // sscanf doesn't do what we want here to we parse this manually.
225  int len = strlen(aSource);
226  char* dest[4] = { aAStr, aBStr, aCStr, aDStr };
227  unsigned destIdx = 0;
228  unsigned destPos = 0;
229 
230  for (int i = 0; i < len; i++)
231  {
232  if (destIdx >= SAL_N_ELEMENTS(dest))
233  {
234  // Invalid format found. Ensure we don't access dest beyond bounds.
235  return false;
236  }
237 
238  if (aSource[i] == '.')
239  {
240  dest[destIdx++][destPos] = 0;
241  destPos = 0;
242  continue;
243  }
244 
245  if (destPos > 3)
246  {
247  // Ignore more than 4 chars. Ensure we never access dest[destIdx]
248  // beyond its bounds.
249  continue;
250  }
251 
252  dest[destIdx][destPos++] = aSource[i];
253  }
254 
255  // Add last terminator.
256  dest[destIdx][destPos] = 0;
257 
258  // Vulkan version numbers have only 3 fields.
259  if (destIdx == SAL_N_ELEMENTS(dest) - 2)
260  dest[++destIdx][0] = '\0';
261  if (destIdx != SAL_N_ELEMENTS(dest) - 1)
262  {
263  return false;
264  }
265  return true;
266 }
267 
268 static bool ParseDriverVersion(const OUString& aVersion, uint64_t& rNumericVersion)
269 {
270  rNumericVersion = 0;
271 
272  int a, b, c, d;
273  char aStr[8], bStr[8], cStr[8], dStr[8];
274  /* honestly, why do I even bother */
275  OString aOVersion = OUStringToOString(aVersion, RTL_TEXTENCODING_UTF8);
276  if (!SplitDriverVersion(aOVersion.getStr(), aStr, bStr, cStr, dStr))
277  return false;
278 
279  PadDriverDecimal(bStr);
280  PadDriverDecimal(cStr);
281  PadDriverDecimal(dStr);
282 
283  a = atoi(aStr);
284  b = atoi(bStr);
285  c = atoi(cStr);
286  d = atoi(dStr);
287 
288  if (a < 0 || a > 0xffff)
289  return false;
290  if (b < 0 || b > 0xffff)
291  return false;
292  if (c < 0 || c > 0xffff)
293  return false;
294  if (d < 0 || d > 0xffff)
295  return false;
296 
297  rNumericVersion = GFX_DRIVER_VERSION(a, b, c, d);
298  return true;
299 }
300 
301 static uint64_t getVersion(const OString& rString)
302 {
303  OUString aString = OStringToOUString(rString, RTL_TEXTENCODING_UTF8);
304  uint64_t nVersion;
305  bool bResult = ParseDriverVersion(aString, nVersion);
306 
307  if (!bResult)
308  {
309  throw InvalidFileException();
310  }
311 
312  return nVersion;
313 }
314 
316 {
317  int nLevel = 1;
318  bool bInMsg = false;
319 
320  while (true)
321  {
323  int nsId;
324 
326  = rReader.nextItem(xmlreader::XmlReader::Text::Normalized, &name, &nsId);
327 
329  {
330  ++nLevel;
331  if (nLevel > 2)
332  throw InvalidFileException();
333 
334  if (name == "msg")
335  {
336  bInMsg = true;
337  }
338  else if (name == "device")
339  {
340  int nsIdDeveice;
341  while (rReader.nextAttribute(&nsIdDeveice, &name))
342  {
343  if (name == "id")
344  {
345  name = rReader.getAttributeValue(false);
346  OString aDeviceId(name.begin, name.length);
347  rDriver.maDevices.push_back(
348  OStringToOUString(aDeviceId, RTL_TEXTENCODING_UTF8));
349  }
350  }
351  }
352  else
353  throw InvalidFileException();
354  }
355  else if (res == xmlreader::XmlReader::Result::End)
356  {
357  --nLevel;
358  bInMsg = false;
359  if (!nLevel)
360  break;
361  }
362  else if (res == xmlreader::XmlReader::Result::Text)
363  {
364  if (bInMsg)
365  {
366  OString sMsg(name.begin, name.length);
367  rDriver.maMsg = OStringToOUString(sMsg, RTL_TEXTENCODING_UTF8);
368  }
369  }
370  }
371 }
372 
374 {
376  {
377  rDriver.mbWhitelisted = true;
378  }
379  else if (meBlockType == BlockType::BLACKLIST)
380  {
381  rDriver.mbWhitelisted = false;
382  }
383  else if (meBlockType == BlockType::UNKNOWN)
384  {
385  throw InvalidFileException();
386  }
387 
389  int nsId;
390 
391  while (rReader.nextAttribute(&nsId, &name))
392  {
393  if (name == "os")
394  {
395  name = rReader.getAttributeValue(false);
396  OString sOS(name.begin, name.length);
397  rDriver.meOperatingSystem = getOperatingSystem(sOS);
398  }
399  else if (name == "vendor")
400  {
401  name = rReader.getAttributeValue(false);
402  OString sVendor(name.begin, name.length);
403  rDriver.maAdapterVendor = GetVendorId(sVendor);
404  }
405  else if (name == "compare")
406  {
407  name = rReader.getAttributeValue(false);
408  OString sCompare(name.begin, name.length);
409  rDriver.meComparisonOp = getComparison(sCompare);
410  }
411  else if (name == "version")
412  {
413  name = rReader.getAttributeValue(false);
414  OString sVersion(name.begin, name.length);
415  rDriver.mnDriverVersion = getVersion(sVersion);
416  }
417  else if (name == "minVersion")
418  {
419  name = rReader.getAttributeValue(false);
420  OString sMinVersion(name.begin, name.length);
421  rDriver.mnDriverVersion = getVersion(sMinVersion);
422  }
423  else if (name == "maxVersion")
424  {
425  name = rReader.getAttributeValue(false);
426  OString sMaxVersion(name.begin, name.length);
427  rDriver.mnDriverVersionMax = getVersion(sMaxVersion);
428  }
429  else
430  {
431  OString aAttrName(name.begin, name.length);
432  SAL_WARN("vcl.driver", "unsupported attribute: " << aAttrName);
433  }
434  }
435 
436  handleDevices(rDriver, rReader);
437 }
438 
440 {
442  int nsId;
443 
444  while (true)
445  {
447  = rReader.nextItem(xmlreader::XmlReader::Text::NONE, &name, &nsId);
448 
450  {
451  if (name == "entry")
452  {
453  DriverInfo aDriver;
454  handleEntry(aDriver, rReader);
455  mrDriverList.push_back(aDriver);
456  }
457  else if (name == "entryRange")
458  {
459  DriverInfo aDriver;
460  handleEntry(aDriver, rReader);
461  mrDriverList.push_back(aDriver);
462  }
463  else
464  {
465  throw InvalidFileException();
466  }
467  }
468  else if (res == xmlreader::XmlReader::Result::End)
469  {
470  break;
471  }
472  }
473 }
474 
476 {
477  while (true)
478  {
480  int nsId;
481 
483  = rReader.nextItem(xmlreader::XmlReader::Text::NONE, &name, &nsId);
484 
486  {
487  if (name == "whitelist")
488  {
490  handleList(rReader);
491  }
492  else if (name == "blacklist")
493  {
495  handleList(rReader);
496  }
497  else if (name == "root")
498  {
499  }
500  else
501  {
502  throw InvalidFileException();
503  }
504  }
505  else if (res == xmlreader::XmlReader::Result::End)
506  {
507  if (name == "whitelist" || name == "blacklist")
508  {
510  }
511  }
512  else if (res == xmlreader::XmlReader::Result::Done)
513  {
514  break;
515  }
516  }
517 }
518 
520 {
521 #ifdef _WIN32
522  // OS version in 16.16 major/minor form
523  // based on http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
524  switch (DriverBlocklist::GetWindowsVersion())
525  {
526  case 0x00060001:
527  return DRIVER_OS_WINDOWS_7;
528  case 0x00060002:
529  return DRIVER_OS_WINDOWS_8;
530  case 0x00060003:
531  return DRIVER_OS_WINDOWS_8_1;
532  case 0x000A0000: // Major 10 Minor 0
533  return DRIVER_OS_WINDOWS_10;
534  default:
535  return DRIVER_OS_UNKNOWN;
536  }
537 #elif defined LINUX
538  return DRIVER_OS_LINUX;
539 #else
540  return DRIVER_OS_UNKNOWN;
541 #endif
542 }
543 
544 namespace
545 {
546 struct compareIgnoreAsciiCase
547 {
548  explicit compareIgnoreAsciiCase(const OUString& rString)
549  : maString(rString)
550  {
551  }
552 
553  bool operator()(const OUString& rCompare) { return maString.equalsIgnoreAsciiCase(rCompare); }
554 
555 private:
556  OUString maString;
557 };
558 }
559 
560 const uint64_t allDriverVersions = ~(uint64_t(0));
561 
563  : meOperatingSystem(DRIVER_OS_UNKNOWN)
564  , maAdapterVendor(GetVendorId(VendorAll))
565  , mbWhitelisted(false)
566  , meComparisonOp(DRIVER_COMPARISON_IGNORED)
567  , mnDriverVersion(0)
568  , mnDriverVersionMax(0)
569 {
570 }
571 
573  uint64_t driverVersion, bool bWhitelisted,
574  const char* suggestedVersion /* = nullptr */)
575  : meOperatingSystem(os)
576  , maAdapterVendor(vendor)
577  , mbWhitelisted(bWhitelisted)
578  , meComparisonOp(op)
579  , mnDriverVersion(driverVersion)
580  , mnDriverVersionMax(0)
581 {
582  if (suggestedVersion)
583  maSuggestedVersion = OStringToOUString(OString(suggestedVersion), RTL_TEXTENCODING_UTF8);
584 }
585 
586 bool FindBlocklistedDeviceInList(std::vector<DriverInfo>& aDeviceInfos,
587  OUString const& sDriverVersion, OUString const& sAdapterVendorID,
588  OUString const& sAdapterDeviceID, OperatingSystem system,
589  const OUString& blocklistURL)
590 {
591  uint64_t driverVersion;
592  ParseDriverVersion(sDriverVersion, driverVersion);
593 
594  bool match = false;
595  for (std::vector<DriverInfo>::size_type i = 0; i < aDeviceInfos.size(); i++)
596  {
597  if (aDeviceInfos[i].meOperatingSystem != DRIVER_OS_ALL
598  && aDeviceInfos[i].meOperatingSystem != system)
599  {
600  continue;
601  }
602 
603  if (!aDeviceInfos[i].maAdapterVendor.equalsIgnoreAsciiCase(GetVendorId(VendorAll))
604  && !aDeviceInfos[i].maAdapterVendor.equalsIgnoreAsciiCase(sAdapterVendorID))
605  {
606  continue;
607  }
608 
609  if (std::none_of(aDeviceInfos[i].maDevices.begin(), aDeviceInfos[i].maDevices.end(),
610  compareIgnoreAsciiCase("all"))
611  && std::none_of(aDeviceInfos[i].maDevices.begin(), aDeviceInfos[i].maDevices.end(),
612  compareIgnoreAsciiCase(sAdapterDeviceID)))
613  {
614  continue;
615  }
616 
617  switch (aDeviceInfos[i].meComparisonOp)
618  {
619  case DRIVER_LESS_THAN:
620  match = driverVersion < aDeviceInfos[i].mnDriverVersion;
621  break;
623  match = driverVersion <= aDeviceInfos[i].mnDriverVersion;
624  break;
625  case DRIVER_GREATER_THAN:
626  match = driverVersion > aDeviceInfos[i].mnDriverVersion;
627  break;
629  match = driverVersion >= aDeviceInfos[i].mnDriverVersion;
630  break;
631  case DRIVER_EQUAL:
632  match = driverVersion == aDeviceInfos[i].mnDriverVersion;
633  break;
634  case DRIVER_NOT_EQUAL:
635  match = driverVersion != aDeviceInfos[i].mnDriverVersion;
636  break;
638  match = driverVersion > aDeviceInfos[i].mnDriverVersion
639  && driverVersion < aDeviceInfos[i].mnDriverVersionMax;
640  break;
642  match = driverVersion >= aDeviceInfos[i].mnDriverVersion
643  && driverVersion <= aDeviceInfos[i].mnDriverVersionMax;
644  break;
646  match = driverVersion >= aDeviceInfos[i].mnDriverVersion
647  && driverVersion < aDeviceInfos[i].mnDriverVersionMax;
648  break;
650  // We don't have a comparison op, so we match everything.
651  match = true;
652  break;
653  default:
654  SAL_WARN("vcl.driver", "Bogus op in " << blocklistURL);
655  break;
656  }
657 
658  if (match || aDeviceInfos[i].mnDriverVersion == allDriverVersions)
659  {
660  // white listed drivers
661  if (aDeviceInfos[i].mbWhitelisted)
662  {
663  SAL_INFO("vcl.driver", "whitelisted driver");
664  return false;
665  }
666 
667  match = true;
668  if (!aDeviceInfos[i].maSuggestedVersion.isEmpty())
669  {
670  SAL_WARN("vcl.driver", "use : " << aDeviceInfos[i].maSuggestedVersion);
671  }
672  break;
673  }
674  }
675 
676  SAL_INFO("vcl.driver", (match ? "blacklisted" : "not blacklisted") << " in " << blocklistURL);
677  return match;
678 }
679 
680 bool IsDeviceBlocked(const OUString& blocklistURL, const OUString& driverVersion,
681  const OUString& vendorId, const OUString& deviceId)
682 {
683  std::vector<DriverInfo> driverList;
684  Parser parser(blocklistURL, driverList);
685  if (!parser.parse())
686  {
687  SAL_WARN("vcl.driver", "error parsing blacklist " << blocklistURL);
688  return false;
689  }
690  return FindBlocklistedDeviceInList(driverList, driverVersion, vendorId, deviceId,
691  getOperatingSystem(), blocklistURL);
692 }
693 
694 #ifdef _WIN32
695 int32_t GetWindowsVersion()
696 {
697  static int32_t winVersion = [&]() {
698  // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are
699  // subject to manifest-based behavior since Windows 8.1, so give wrong results.
700  // Another approach would be to use NetWkstaGetInfo, but that has some small
701  // reported delays (some milliseconds), and might get slower in domains with
702  // poor network connections.
703  // So go with a solution described at https://msdn.microsoft.com/en-us/library/ms724429
704  HINSTANCE hLibrary = LoadLibraryW(L"kernel32.dll");
705  if (hLibrary != nullptr)
706  {
707  wchar_t szPath[MAX_PATH];
708  DWORD dwCount = GetModuleFileNameW(hLibrary, szPath, SAL_N_ELEMENTS(szPath));
709  FreeLibrary(hLibrary);
710  if (dwCount != 0 && dwCount < SAL_N_ELEMENTS(szPath))
711  {
712  dwCount = GetFileVersionInfoSizeW(szPath, nullptr);
713  if (dwCount != 0)
714  {
715  std::unique_ptr<char[]> ver(new char[dwCount]);
716  if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE)
717  {
718  void* pBlock = nullptr;
719  UINT dwBlockSz = 0;
720  if (VerQueryValueW(ver.get(), L"\\", &pBlock, &dwBlockSz) != FALSE
721  && dwBlockSz >= sizeof(VS_FIXEDFILEINFO))
722  {
723  VS_FIXEDFILEINFO* vinfo = static_cast<VS_FIXEDFILEINFO*>(pBlock);
724  return int32_t(vinfo->dwProductVersionMS);
725  }
726  }
727  }
728  }
729  }
730  return 0;
731  }();
732 
733  return winVersion;
734 }
735 #endif
736 
737 } // namespace
738 
739 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
VersionComparisonOp meComparisonOp
std::vector< OUString > maDevices
tuple parser
static OUString GetVendorId(const OString &rString)
uint32_t vendorId
static void PadDriverDecimal(char *aString)
char const * begin
DeviceVendor GetVendorFromId(uint32_t id)
Returns vendor for the given vendor ID, or VendorAll if not known.
bool match(const sal_Unicode *pWild, const sal_Unicode *pStr, const sal_Unicode cEscape)
static uint64_t getVersion(const OString &rString)
OUString maString
OUStringLiteral GetVendorNameFromId(uint32_t id)
static bool ParseDriverVersion(const OUString &aVersion, uint64_t &rNumericVersion)
#define GFX_DRIVER_VERSION(a, b, c, d)
UNKNOWN
const uint64_t allDriverVersions
double d
static OperatingSystem getOperatingSystem(const OString &rString)
#define SAL_N_ELEMENTS(arr)
bool IsDeviceBlocked(const OUString &blocklistURL, const OUString &driverVersion, const OUString &vendorId, const OUString &deviceId)
int i
void handleContent(xmlreader::XmlReader &rReader)
uno_Any a
static VersionComparisonOp getComparison(const OString &rString)
sal_Int16 nVersion
OString OUStringToOString(const OUString &str, ConnectionSettings const *settings)
const int DeviceVendorMax
sal_Int32 length
Parser(const OUString &rURL, std::vector< DriverInfo > &rDriverList)
Span getAttributeValue(bool fullyNormalize)
#define SAL_INFO(area, stream)
static void handleDevices(DriverInfo &rDriver, xmlreader::XmlReader &rReader)
void handleEntry(DriverInfo &rDriver, xmlreader::XmlReader &rReader)
bool FindBlocklistedDeviceInList(std::vector< DriverInfo > &aDeviceInfos, OUString const &sDriverVersion, OUString const &sAdapterVendorID, OUString const &sAdapterDeviceID, OperatingSystem system, const OUString &blocklistURL)
const char * name
#define MAX_PATH
static bool SplitDriverVersion(const char *aSource, char *aAStr, char *aBStr, char *aCStr, char *aDStr)
void handleList(xmlreader::XmlReader &rReader)
bool nextAttribute(int *nsId, Span *localName)
#define SAL_WARN(area, stream)
Result nextItem(Text reportText, Span *data, int *nsId)
std::vector< DriverInfo > & mrDriverList
aStr