1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include <windows.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include "gtest/gtest.h"
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/Unused.h"
13 #include "mozilla/WindowsVersion.h"
14 #include "nsDependentString.h"
15 #include "nsReadableUtils.h"
16 #include "nsUnicharUtils.h"
17 #include "nsWindowsHelpers.h"
18
19 using namespace mozilla;
20
21 constexpr const WCHAR pattern[] = L"\\api-*.dll";
22
GetModuleFileDir(HMODULE module,LPWSTR path,DWORD size)23 static LPWSTR GetModuleFileDir(HMODULE module, LPWSTR path, DWORD size) {
24 DWORD chars = GetModuleFileNameW(module, path, size);
25 if (chars <= 0 || chars >= MAX_PATH) {
26 return nullptr;
27 }
28
29 // Split the base name from the directory.
30 LPWSTR basename = wcsrchr(path, L'\\');
31 if (!basename) {
32 return nullptr; // at least one path separator must be present
33 }
34 *basename++ = L'\0';
35 return basename;
36 }
37
38 // Make sure that Universal CRT forwarder DLLs are not in app directory if it
39 // is in Api Sets.
TEST(TestUCRTDepends,AppDir)40 TEST(TestUCRTDepends, AppDir)
41 {
42 WCHAR appdir[MAX_PATH];
43 ASSERT_TRUE(GetModuleFileDir(nullptr, appdir, MAX_PATH));
44
45 WCHAR path[MAX_PATH + ArrayLength(pattern)];
46 swprintf(path, L"%s%s", appdir, pattern);
47
48 WIN32_FIND_DATAW wfd;
49 HANDLE hFind = FindFirstFileW(path, &wfd);
50 #if defined(_M_ARM64) // We do not ship Universal CRT DLLs on aarch64.
51 if (hFind == INVALID_HANDLE_VALUE) {
52 EXPECT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND);
53 return;
54 }
55 #else
56 ASSERT_NE(hFind, INVALID_HANDLE_VALUE);
57 #endif
58 do {
59 nsModuleHandle module(LoadLibraryW(wfd.cFileName));
60 EXPECT_TRUE(module);
61 if (!module) {
62 continue;
63 }
64
65 // Get a full path of the loaded module.
66 LPWSTR basename = GetModuleFileDir(module, path, MAX_PATH);
67 ASSERT_TRUE(basename);
68
69 // If the module is in Api Sets, GetModuleFileName returns the redirected
70 // DLL path, so filenames will not match.
71 bool inApiSets = wcsicmp(wfd.cFileName, basename);
72 if (IsWin10OrLater()) {
73 // All files must be in Api Sets on Windows 10.
74 EXPECT_TRUE(inApiSets);
75 continue;
76 }
77 if (IsWin8OrLater()) {
78 if (inApiSets) {
79 continue; // This file is in Api Sets, OK.
80 }
81 // Universal CRT files are not in Api Sets on Windows 8.
82 EXPECT_TRUE(StringBeginsWith(nsDependentString(wfd.cFileName),
83 u"api-ms-win-crt-"_ns,
84 nsCaseInsensitiveStringComparator));
85 } else { // Windows 7
86 // All files must not be in Api Sets on Windows 7.
87 EXPECT_FALSE(inApiSets);
88 }
89 // Files must be loaded from appdir
90 EXPECT_TRUE(!wcsicmp(path, appdir));
91 } while (FindNextFileW(hFind, &wfd));
92 EXPECT_EQ(GetLastError(), ERROR_NO_MORE_FILES);
93 BOOL ret = FindClose(hFind);
94 EXPECT_TRUE(ret);
95 }
96
97 // Make sure that we do not depend on Universal CRT forwarder DLLs in the
98 // system directory.
TEST(TestUCRTDepends,SystemDir)99 TEST(TestUCRTDepends, SystemDir)
100 {
101 WCHAR appdir[MAX_PATH];
102 ASSERT_TRUE(GetModuleFileDir(nullptr, appdir, MAX_PATH));
103
104 WCHAR path[MAX_PATH + ArrayLength(pattern)];
105 UINT chars = GetSystemDirectoryW(path, MAX_PATH);
106 ASSERT_TRUE(chars > 0 && chars < MAX_PATH);
107 wcscat(path, pattern);
108
109 WIN32_FIND_DATAW wfd;
110 HANDLE hFind = FindFirstFileW(path, &wfd);
111 if (hFind == INVALID_HANDLE_VALUE) {
112 EXPECT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND);
113 EXPECT_TRUE(IsWin8OrLater());
114 return; // Not found in the system directory, OK.
115 }
116 // Api Sets forwarders must not be present on Windows 10.
117 EXPECT_FALSE(IsWin10OrLater());
118 do {
119 HMODULE module = GetModuleHandleW(wfd.cFileName);
120 if (!module) {
121 continue; // We are not using this file, OK.
122 }
123
124 // Get a full path of the loaded module.
125 LPWSTR basename = GetModuleFileDir(module, path, MAX_PATH);
126 ASSERT_TRUE(basename);
127
128 // If the module is in Api Sets, GetModuleFileName returns the redirected
129 // DLL path, so filenames will not match.
130 if (wcsicmp(wfd.cFileName, basename)) {
131 // If this file is in Api Sets, it must not be present in appdir.
132 swprintf(path, L"%s\\%s", appdir, wfd.cFileName);
133 EXPECT_EQ(GetFileAttributesW(path), INVALID_FILE_ATTRIBUTES);
134 } else {
135 // If this file is not in Api Sets, it must be loaded from appdir.
136 EXPECT_TRUE(!wcsicmp(path, appdir));
137 }
138 } while (FindNextFileW(hFind, &wfd));
139 EXPECT_EQ(GetLastError(), ERROR_NO_MORE_FILES);
140 BOOL ret = FindClose(hFind);
141 EXPECT_TRUE(ret);
142 }
143