1 /*
2  * Copyright (c) 2021-2022 The Khronos Group Inc.
3  * Copyright (c) 2021-2022 Valve Corporation
4  * Copyright (c) 2021-2022 LunarG, Inc.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and/or associated documentation files (the "Materials"), to
8  * deal in the Materials without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Materials, and to permit persons to whom the Materials are
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice(s) and this permission notice shall be included in
14  * all copies or substantial portions of the Materials.
15  *
16  * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  *
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
23  * USE OR OTHER DEALINGS IN THE MATERIALS.
24  *
25  * Author: Charles Giessen <charles@lunarg.com>
26  */
27 
28 #include "shim.h"
29 
30 #include <algorithm>
31 
32 #if defined(__APPLE__)
33 #include <CoreFoundation/CoreFoundation.h>
34 #endif
35 
36 PlatformShim platform_shim;
37 extern "C" {
38 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__)
get_platform_shim(std::vector<fs::FolderManager>* folders)39 PlatformShim* get_platform_shim(std::vector<fs::FolderManager>* folders) {
40     platform_shim = PlatformShim(folders);
41     return &platform_shim;
42 }
43 #elif defined(__APPLE__)
get_platform_shim(std::vector<fs::FolderManager>* folders)44 FRAMEWORK_EXPORT PlatformShim* get_platform_shim(std::vector<fs::FolderManager>* folders) {
45     platform_shim = PlatformShim(folders);
46     return &platform_shim;
47 }
48 #endif
49 
50 // Necessary for MacOS function shimming
51 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__)
52 #define OPENDIR_FUNC_NAME opendir
53 #define READDIR_FUNC_NAME readdir
54 #define CLOSEDIR_FUNC_NAME closedir
55 #define ACCESS_FUNC_NAME access
56 #define FOPEN_FUNC_NAME fopen
57 #define DLOPEN_FUNC_NAME dlopen
58 #define GETEUID_FUNC_NAME geteuid
59 #define GETEGID_FUNC_NAME getegid
60 #if defined(HAVE_SECURE_GETENV)
61 #define SECURE_GETENV_FUNC_NAME secure_getenv
62 #endif
63 #if defined(HAVE___SECURE_GETENV)
64 #define __SECURE_GETENV_FUNC_NAME __secure_getenv
65 #endif
66 #elif defined(__APPLE__)
67 #define OPENDIR_FUNC_NAME my_opendir
68 #define READDIR_FUNC_NAME my_readdir
69 #define CLOSEDIR_FUNC_NAME my_closedir
70 #define ACCESS_FUNC_NAME my_access
71 #define FOPEN_FUNC_NAME my_fopen
72 #define DLOPEN_FUNC_NAME my_dlopen
73 #define GETEUID_FUNC_NAME my_geteuid
74 #define GETEGID_FUNC_NAME my_getegid
75 #if !defined(TARGET_OS_IPHONE)
76 #if defined(HAVE_SECURE_GETENV)
77 #define SECURE_GETENV_FUNC_NAME my_secure_getenv
78 #endif
79 #if defined(HAVE___SECURE_GETENV)
80 #define __SECURE_GETENV_FUNC_NAME my__secure_getenv
81 #endif
82 #endif
83 #endif
84 
85 using PFN_OPENDIR = DIR* (*)(const char* path_name);
86 using PFN_READDIR = struct dirent* (*)(DIR* dir_stream);
87 using PFN_CLOSEDIR = int (*)(DIR* dir_stream);
88 using PFN_ACCESS = int (*)(const char* pathname, int mode);
89 using PFN_FOPEN = FILE* (*)(const char* filename, const char* mode);
90 using PFN_DLOPEN = void* (*)(const char* in_filename, int flags);
91 using PFN_GETEUID = uid_t (*)(void);
92 using PFN_GETEGID = gid_t (*)(void);
93 #if defined(HAVE_SECURE_GETENV) || defined(HAVE___SECURE_GETENV)
94 using PFN_SEC_GETENV = char* (*)(const char* name);
95 #endif
96 
97 #if defined(__APPLE__)
98 #define real_opendir opendir
99 #define real_readdir readdir
100 #define real_closedir closedir
101 #define real_access access
102 #define real_fopen fopen
103 #define real_dlopen dlopen
104 #define real_geteuid geteuid
105 #define real_getegid getegid
106 #if defined(HAVE_SECURE_GETENV)
107 #define real_secure_getenv secure_getenv
108 #endif
109 #if defined(HAVE___SECURE_GETENV)
110 #define real__secure_getenv __secure_getenv
111 #endif
112 #else
113 PFN_OPENDIR real_opendir = nullptr;
114 PFN_READDIR real_readdir = nullptr;
115 PFN_CLOSEDIR real_closedir = nullptr;
116 PFN_ACCESS real_access = nullptr;
117 PFN_FOPEN real_fopen = nullptr;
118 PFN_DLOPEN real_dlopen = nullptr;
119 PFN_GETEUID real_geteuid = nullptr;
120 PFN_GETEGID real_getegid = nullptr;
121 #if defined(HAVE_SECURE_GETENV)
122 PFN_SEC_GETENV real_secure_getenv = nullptr;
123 #endif
124 #if defined(HAVE___SECURE_GETENV)
125 PFN_SEC_GETENV real__secure_getenv = nullptr;
126 #endif
127 #endif
128 
OPENDIR_FUNC_NAME(const char* path_name)129 FRAMEWORK_EXPORT DIR* OPENDIR_FUNC_NAME(const char* path_name) {
130 #if !defined(__APPLE__)
131     if (!real_opendir) real_opendir = (PFN_OPENDIR)dlsym(RTLD_NEXT, "opendir");
132 #endif
133     if (platform_shim.is_during_destruction) {
134         return real_opendir(path_name);
135     }
136     DIR* dir;
137     if (platform_shim.is_fake_path(path_name)) {
138         auto real_path_name = platform_shim.get_real_path_from_fake_path(fs::path(path_name));
139         dir = real_opendir(real_path_name.c_str());
140         platform_shim.dir_entries.push_back(DirEntry{dir, std::string(path_name), {}, 0, true});
141     } else if (platform_shim.is_known_path(path_name)) {
142         dir = real_opendir(path_name);
143         platform_shim.dir_entries.push_back(DirEntry{dir, std::string(path_name), {}, 0, false});
144     } else {
145         dir = real_opendir(path_name);
146     }
147 
148     return dir;
149 }
150 
READDIR_FUNC_NAME(DIR* dir_stream)151 FRAMEWORK_EXPORT struct dirent* READDIR_FUNC_NAME(DIR* dir_stream) {
152 #if !defined(__APPLE__)
153     if (!real_readdir) real_readdir = (PFN_READDIR)dlsym(RTLD_NEXT, "readdir");
154 #endif
155     if (platform_shim.is_during_destruction) {
156         return real_readdir(dir_stream);
157     }
158     auto it = std::find_if(platform_shim.dir_entries.begin(), platform_shim.dir_entries.end(),
159                            [dir_stream](DirEntry const& entry) { return entry.directory == dir_stream; });
160 
161     if (it == platform_shim.dir_entries.end()) {
162         return real_readdir(dir_stream);
163     }
164     // Folder was found but this is the first file to be read from it
165     if (it->current_index == 0) {
166         std::vector<struct dirent*> folder_contents;
167         std::vector<std::string> dirent_filenames;
168         while (true) {
169             struct dirent* dir_entry = real_readdir(dir_stream);
170             if (NULL == dir_entry) {
171                 break;
172             }
173             folder_contents.push_back(dir_entry);
174             dirent_filenames.push_back(&dir_entry->d_name[0]);
175         }
176         auto real_path = it->folder_path;
177         if (it->is_fake_path) {
178             real_path = platform_shim.redirection_map.at(it->folder_path).str();
179         }
180         auto filenames = get_folder_contents(platform_shim.folders, real_path);
181 
182         // Add the dirent structures in the order they appear in the FolderManager
183         // Ignore anything which wasn't in the FolderManager
184         for (auto const& file : filenames) {
185             for (size_t i = 0; i < dirent_filenames.size(); i++) {
186                 if (file == dirent_filenames.at(i)) {
187                     it->contents.push_back(folder_contents.at(i));
188                     break;
189                 }
190             }
191         }
192     }
193     if (it->current_index >= it->contents.size()) return nullptr;
194     return it->contents.at(it->current_index++);
195 }
196 
CLOSEDIR_FUNC_NAME(DIR* dir_stream)197 FRAMEWORK_EXPORT int CLOSEDIR_FUNC_NAME(DIR* dir_stream) {
198 #if !defined(__APPLE__)
199     if (!real_closedir) real_closedir = (PFN_CLOSEDIR)dlsym(RTLD_NEXT, "closedir");
200 #endif
201     if (platform_shim.is_during_destruction) {
202         return real_closedir(dir_stream);
203     }
204     auto it = std::find_if(platform_shim.dir_entries.begin(), platform_shim.dir_entries.end(),
205                            [dir_stream](DirEntry const& entry) { return entry.directory == dir_stream; });
206 
207     if (it != platform_shim.dir_entries.end()) {
208         platform_shim.dir_entries.erase(it);
209     }
210 
211     return real_closedir(dir_stream);
212 }
213 
ACCESS_FUNC_NAME(const char* in_pathname, int mode)214 FRAMEWORK_EXPORT int ACCESS_FUNC_NAME(const char* in_pathname, int mode) {
215 #if !defined(__APPLE__)
216     if (!real_access) real_access = (PFN_ACCESS)dlsym(RTLD_NEXT, "access");
217 #endif
218     fs::path path{in_pathname};
219     if (!path.has_parent_path()) {
220         return real_access(in_pathname, mode);
221     }
222 
223     if (platform_shim.is_fake_path(path.parent_path())) {
224         fs::path real_path = platform_shim.get_real_path_from_fake_path(path.parent_path());
225         real_path /= path.filename();
226         return real_access(real_path.c_str(), mode);
227     }
228     return real_access(in_pathname, mode);
229 }
230 
FOPEN_FUNC_NAME(const char* in_filename, const char* mode)231 FRAMEWORK_EXPORT FILE* FOPEN_FUNC_NAME(const char* in_filename, const char* mode) {
232 #if !defined(__APPLE__)
233     if (!real_fopen) real_fopen = (PFN_FOPEN)dlsym(RTLD_NEXT, "fopen");
234 #endif
235     fs::path path{in_filename};
236     if (!path.has_parent_path()) {
237         return real_fopen(in_filename, mode);
238     }
239 
240     FILE* f_ptr;
241     if (platform_shim.is_fake_path(path.parent_path())) {
242         auto real_path = platform_shim.get_real_path_from_fake_path(path.parent_path()) / path.filename();
243         f_ptr = real_fopen(real_path.c_str(), mode);
244     } else {
245         f_ptr = real_fopen(in_filename, mode);
246     }
247 
248     return f_ptr;
249 }
250 
DLOPEN_FUNC_NAME(const char* in_filename, int flags)251 FRAMEWORK_EXPORT void* DLOPEN_FUNC_NAME(const char* in_filename, int flags) {
252 #if !defined(__APPLE__)
253     if (!real_dlopen) real_dlopen = (PFN_DLOPEN)dlsym(RTLD_NEXT, "dlopen");
254 #endif
255 
256     if (platform_shim.is_dlopen_redirect_name(in_filename)) {
257         return real_dlopen(platform_shim.dlopen_redirection_map[in_filename].c_str(), flags);
258     }
259     return real_dlopen(in_filename, flags);
260 }
261 
GETEUID_FUNC_NAME(void)262 FRAMEWORK_EXPORT uid_t GETEUID_FUNC_NAME(void) {
263 #if !defined(__APPLE__)
264     if (!real_geteuid) real_geteuid = (PFN_GETEUID)dlsym(RTLD_NEXT, "geteuid");
265 #endif
266     if (platform_shim.use_fake_elevation) {
267         // Root on linux is 0, so force pretending like we're root
268         return 0;
269     } else {
270         return real_geteuid();
271     }
272 }
273 
GETEGID_FUNC_NAME(void)274 FRAMEWORK_EXPORT gid_t GETEGID_FUNC_NAME(void) {
275 #if !defined(__APPLE__)
276     if (!real_getegid) real_getegid = (PFN_GETEGID)dlsym(RTLD_NEXT, "getegid");
277 #endif
278     if (platform_shim.use_fake_elevation) {
279         // Root on linux is 0, so force pretending like we're root
280         return 0;
281     } else {
282         return real_getegid();
283     }
284 }
285 
286 #if !defined(TARGET_OS_IPHONE)
287 #if defined(HAVE_SECURE_GETENV)
SECURE_GETENV_FUNC_NAME(const char* name)288 FRAMEWORK_EXPORT char* SECURE_GETENV_FUNC_NAME(const char* name) {
289 #if !defined(__APPLE__)
290     if (!real_secure_getenv) real_secure_getenv = (PFN_SEC_GETENV)dlsym(RTLD_NEXT, "secure_getenv");
291 #endif
292     if (platform_shim.use_fake_elevation) {
293         return NULL;
294     } else {
295         return real_secure_getenv(name);
296     }
297 }
298 #endif
299 #if defined(HAVE___SECURE_GETENV)
__SECURE_GETENV_FUNC_NAME(const char* name)300 FRAMEWORK_EXPORT char* __SECURE_GETENV_FUNC_NAME(const char* name) {
301 #if !defined(__APPLE__)
302     if (!real__secure_getenv) real__secure_getenv = (PFN_SEC_GETENV)dlsym(RTLD_NEXT, "__secure_getenv");
303 #endif
304 
305     if (platform_shim.use_fake_elevation) {
306         return NULL;
307     } else {
308         return real__secure_getenv(name);
309     }
310 }
311 #endif
312 #endif
313 #if defined(__APPLE__)
my_CFBundleGetMainBundle()314 FRAMEWORK_EXPORT CFBundleRef my_CFBundleGetMainBundle() {
315     static CFBundleRef global_bundle{};
316     return reinterpret_cast<CFBundleRef>(&global_bundle);
317 }
my_CFBundleCopyResourcesDirectoryURL([[maybe_unused]] CFBundleRef bundle)318 FRAMEWORK_EXPORT CFURLRef my_CFBundleCopyResourcesDirectoryURL([[maybe_unused]] CFBundleRef bundle) {
319     static CFURLRef global_url{};
320     return reinterpret_cast<CFURLRef>(&global_url);
321 }
my_CFURLGetFileSystemRepresentation([[maybe_unused]] CFURLRef url, [[maybe_unused]] Boolean resolveAgainstBase, UInt8* buffer, CFIndex maxBufLen)322 FRAMEWORK_EXPORT Boolean my_CFURLGetFileSystemRepresentation([[maybe_unused]] CFURLRef url,
323                                                              [[maybe_unused]] Boolean resolveAgainstBase, UInt8* buffer,
324                                                              CFIndex maxBufLen) {
325     if (!platform_shim.bundle_contents.empty()) {
326         CFIndex copy_len = (CFIndex)platform_shim.bundle_contents.size();
327         if (copy_len > maxBufLen) {
328             copy_len = maxBufLen;
329         }
330         strncpy(reinterpret_cast<char*>(buffer), platform_shim.bundle_contents.c_str(), copy_len);
331         return TRUE;
332     }
333     return FALSE;
334 }
335 #endif
336 
337 /* Shiming functions on apple is limited by the linker prefering to not use functions in the
338  * executable in loaded dylibs. By adding an interposer, we redirect the linker to use our
339  * version of the function over the real one, thus shimming the system function.
340  */
341 #if defined(__APPLE__)
342 #define MACOS_ATTRIB __attribute__((section("__DATA,__interpose")))
343 #define VOIDP_CAST(_func) reinterpret_cast<const void*>(&_func)
344 
345 struct Interposer {
346     const void* shim_function;
347     const void* underlying_function;
348 };
349 
350 __attribute__((used)) static Interposer _interpose_opendir MACOS_ATTRIB = {VOIDP_CAST(my_opendir), VOIDP_CAST(opendir)};
351 // don't intercept readdir as it crashes when using ASAN with macOS
352 // __attribute__((used)) static Interposer _interpose_readdir MACOS_ATTRIB = {VOIDP_CAST(my_readdir), VOIDP_CAST(readdir)};
353 __attribute__((used)) static Interposer _interpose_closedir MACOS_ATTRIB = {VOIDP_CAST(my_closedir), VOIDP_CAST(closedir)};
354 __attribute__((used)) static Interposer _interpose_access MACOS_ATTRIB = {VOIDP_CAST(my_access), VOIDP_CAST(access)};
355 __attribute__((used)) static Interposer _interpose_fopen MACOS_ATTRIB = {VOIDP_CAST(my_fopen), VOIDP_CAST(fopen)};
356 __attribute__((used)) static Interposer _interpose_dlopen MACOS_ATTRIB = {VOIDP_CAST(my_dlopen), VOIDP_CAST(dlopen)};
357 __attribute__((used)) static Interposer _interpose_euid MACOS_ATTRIB = {VOIDP_CAST(my_geteuid), VOIDP_CAST(geteuid)};
358 __attribute__((used)) static Interposer _interpose_egid MACOS_ATTRIB = {VOIDP_CAST(my_getegid), VOIDP_CAST(getegid)};
359 #if !defined(TARGET_OS_IPHONE)
360 #if defined(HAVE_SECURE_GETENV)
361 __attribute__((used)) static Interposer _interpose_secure_getenv MACOS_ATTRIB = {VOIDP_CAST(my_secure_getenv),
362                                                                                  VOIDP_CAST(secure_getenv)};
363 #endif
364 #if defined(HAVE___SECURE_GETENV)
365 __attribute__((used)) static Interposer _interpose__secure_getenv MACOS_ATTRIB = {VOIDP_CAST(my__secure_getenv),
366                                                                                   VOIDP_CAST(__secure_getenv)};
367 #endif
368 #endif
369 __attribute__((used)) static Interposer _interpose_CFBundleGetMainBundle MACOS_ATTRIB = {VOIDP_CAST(my_CFBundleGetMainBundle),
370                                                                                          VOIDP_CAST(CFBundleGetMainBundle)};
371 __attribute__((used)) static Interposer _interpose_CFBundleCopyResourcesDirectoryURL MACOS_ATTRIB = {
372     VOIDP_CAST(my_CFBundleCopyResourcesDirectoryURL), VOIDP_CAST(CFBundleCopyResourcesDirectoryURL)};
373 __attribute__((used)) static Interposer _interpose_CFURLGetFileSystemRepresentation MACOS_ATTRIB = {
374     VOIDP_CAST(my_CFURLGetFileSystemRepresentation), VOIDP_CAST(CFURLGetFileSystemRepresentation)};
375 
376 #endif
377 }  // extern "C"
378