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