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 static PlatformShim platform_shim;
31 extern "C" {
32 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
get_platform_shim(std::vector<fs::FolderManager> * folders)33 PlatformShim* get_platform_shim(std::vector<fs::FolderManager>* folders) {
34 platform_shim = PlatformShim(folders);
35 return &platform_shim;
36 }
37 #elif defined(__APPLE__)
38 FRAMEWORK_EXPORT PlatformShim* get_platform_shim(std::vector<fs::FolderManager>* folders) {
39 platform_shim = PlatformShim(folders);
40 return &platform_shim;
41 }
42 #endif
43
44 // Necessary for MacOS function shimming
45 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
46 #define OPENDIR_FUNC_NAME opendir
47 #define READDIR_FUNC_NAME readdir
48 #define CLOSEDIR_FUNC_NAME closedir
49 #define ACCESS_FUNC_NAME access
50 #define FOPEN_FUNC_NAME fopen
51 #define GETEUID_FUNC_NAME geteuid
52 #define GETEGID_FUNC_NAME getegid
53 #if defined(HAVE_SECURE_GETENV)
54 #define SECURE_GETENV_FUNC_NAME secure_getenv
55 #endif
56 #if defined(HAVE___SECURE_GETENV)
57 #define __SECURE_GETENV_FUNC_NAME __secure_getenv
58 #endif
59 #elif defined(__APPLE__)
60 #define OPENDIR_FUNC_NAME my_opendir
61 #define READDIR_FUNC_NAME my_readdir
62 #define CLOSEDIR_FUNC_NAME my_closedir
63 #define ACCESS_FUNC_NAME my_access
64 #define FOPEN_FUNC_NAME my_fopen
65 #define GETEUID_FUNC_NAME my_geteuid
66 #define GETEGID_FUNC_NAME my_getegid
67 #if defined(HAVE_SECURE_GETENV)
68 #define SECURE_GETENV_FUNC_NAME my_secure_getenv
69 #endif
70 #if defined(HAVE___SECURE_GETENV)
71 #define __SECURE_GETENV_FUNC_NAME my__secure_getenv
72 #endif
73 #endif
74
75 using PFN_OPENDIR = DIR* (*)(const char* path_name);
76 using PFN_READDIR = struct dirent* (*)(DIR* dir_stream);
77 using PFN_CLOSEDIR = int (*)(DIR* dir_stream);
78 using PFN_ACCESS = int (*)(const char* pathname, int mode);
79 using PFN_FOPEN = FILE* (*)(const char* filename, const char* mode);
80 using PFN_GETEUID = uid_t (*)(void);
81 using PFN_GETEGID = gid_t (*)(void);
82 #if defined(HAVE_SECURE_GETENV) || defined(HAVE___SECURE_GETENV)
83 using PFN_SEC_GETENV = char* (*)(const char* name);
84 #endif
85
86 static PFN_OPENDIR real_opendir = nullptr;
87 static PFN_READDIR real_readdir = nullptr;
88 static PFN_CLOSEDIR real_closedir = nullptr;
89 static PFN_ACCESS real_access = nullptr;
90 static PFN_FOPEN real_fopen = nullptr;
91 static PFN_GETEUID real_geteuid = nullptr;
92 static PFN_GETEGID real_getegid = nullptr;
93 #if defined(HAVE_SECURE_GETENV)
94 static PFN_SEC_GETENV real_secure_getenv = nullptr;
95 #endif
96 #if defined(HAVE___SECURE_GETENV)
97 static PFN_SEC_GETENV real__secure_getenv = nullptr;
98 #endif
99
OPENDIR_FUNC_NAME(const char * path_name)100 FRAMEWORK_EXPORT DIR* OPENDIR_FUNC_NAME(const char* path_name) {
101 if (!real_opendir) real_opendir = (PFN_OPENDIR)dlsym(RTLD_NEXT, "opendir");
102
103 DIR* dir;
104 if (platform_shim.is_fake_path(path_name)) {
105 auto fake_path_name = platform_shim.get_fake_path(fs::path(path_name));
106 dir = real_opendir(fake_path_name.c_str());
107 platform_shim.dir_entries.push_back(DirEntry{dir, std::string(path_name), {}, false});
108 } else {
109 dir = real_opendir(path_name);
110 }
111
112 return dir;
113 }
114
READDIR_FUNC_NAME(DIR * dir_stream)115 FRAMEWORK_EXPORT struct dirent* READDIR_FUNC_NAME(DIR* dir_stream) {
116 if (!real_readdir) real_readdir = (PFN_READDIR)dlsym(RTLD_NEXT, "readdir");
117 auto it = std::find_if(platform_shim.dir_entries.begin(), platform_shim.dir_entries.end(),
118 [dir_stream](DirEntry const& entry) { return entry.directory == dir_stream; });
119
120 if (it == platform_shim.dir_entries.end()) {
121 return real_readdir(dir_stream);
122 }
123 // Folder was found but this is the first file to be read from it
124 if (it->current_index == 0) {
125 std::vector<struct dirent*> folder_contents;
126 std::vector<std::string> dirent_filenames;
127 while (true) {
128 struct dirent* dir_entry = real_readdir(dir_stream);
129 if (NULL == dir_entry) {
130 break;
131 }
132 folder_contents.push_back(dir_entry);
133 dirent_filenames.push_back(&dir_entry->d_name[0]);
134 }
135 auto real_path = platform_shim.redirection_map.at(it->folder_path);
136 auto filenames = get_folder_contents(platform_shim.folders, real_path.str());
137
138 // Add the dirent structures in the order they appear in the FolderManager
139 // Ignore anything which wasn't in the FolderManager
140 for (auto const& file : filenames) {
141 for (size_t i = 0; i < dirent_filenames.size(); i++) {
142 if (file == dirent_filenames.at(i)) {
143 it->contents.push_back(folder_contents.at(i));
144 break;
145 }
146 }
147 }
148 }
149 if (it->current_index >= it->contents.size()) return nullptr;
150 return it->contents.at(it->current_index++);
151 }
152
CLOSEDIR_FUNC_NAME(DIR * dir_stream)153 FRAMEWORK_EXPORT int CLOSEDIR_FUNC_NAME(DIR* dir_stream) {
154 if (!real_closedir) real_closedir = (PFN_CLOSEDIR)dlsym(RTLD_NEXT, "closedir");
155
156 auto it = std::find_if(platform_shim.dir_entries.begin(), platform_shim.dir_entries.end(),
157 [dir_stream](DirEntry const& entry) { return entry.directory == dir_stream; });
158
159 if (it != platform_shim.dir_entries.end()) {
160 platform_shim.dir_entries.erase(it);
161 }
162
163 return real_closedir(dir_stream);
164 }
165
ACCESS_FUNC_NAME(const char * in_pathname,int mode)166 FRAMEWORK_EXPORT int ACCESS_FUNC_NAME(const char* in_pathname, int mode) {
167 if (!real_access) real_access = (PFN_ACCESS)dlsym(RTLD_NEXT, "access");
168
169 fs::path path{in_pathname};
170 if (!path.has_parent_path()) {
171 return real_access(in_pathname, mode);
172 }
173
174 if (platform_shim.is_fake_path(path.parent_path())) {
175 fs::path fake_path = platform_shim.get_fake_path(path.parent_path());
176 fake_path /= path.filename();
177 return real_access(fake_path.c_str(), mode);
178 }
179 return real_access(in_pathname, mode);
180 }
181
FOPEN_FUNC_NAME(const char * in_filename,const char * mode)182 FRAMEWORK_EXPORT FILE* FOPEN_FUNC_NAME(const char* in_filename, const char* mode) {
183 if (!real_fopen) real_fopen = (PFN_FOPEN)dlsym(RTLD_NEXT, "fopen");
184
185 fs::path path{in_filename};
186 if (!path.has_parent_path()) {
187 return real_fopen(in_filename, mode);
188 }
189
190 FILE* f_ptr;
191 if (platform_shim.is_fake_path(path.parent_path())) {
192 auto fake_path = platform_shim.get_fake_path(path.parent_path()) / path.filename();
193 f_ptr = real_fopen(fake_path.c_str(), mode);
194 } else {
195 f_ptr = real_fopen(in_filename, mode);
196 }
197
198 return f_ptr;
199 }
200
GETEUID_FUNC_NAME(void)201 FRAMEWORK_EXPORT uid_t GETEUID_FUNC_NAME(void) {
202 if (!real_geteuid) real_geteuid = (PFN_GETEUID)dlsym(RTLD_NEXT, "geteuid");
203
204 if (platform_shim.use_fake_elevation) {
205 // Root on linux is 0, so force pretending like we're root
206 return 0;
207 } else {
208 return real_geteuid();
209 }
210 }
211
GETEGID_FUNC_NAME(void)212 FRAMEWORK_EXPORT gid_t GETEGID_FUNC_NAME(void) {
213 if (!real_getegid) real_getegid = (PFN_GETEGID)dlsym(RTLD_NEXT, "getegid");
214
215 if (platform_shim.use_fake_elevation) {
216 // Root on linux is 0, so force pretending like we're root
217 return 0;
218 } else {
219 return real_getegid();
220 }
221 }
222
223 #if defined(HAVE_SECURE_GETENV)
SECURE_GETENV_FUNC_NAME(const char * name)224 FRAMEWORK_EXPORT char* SECURE_GETENV_FUNC_NAME(const char* name) {
225 if (!real_secure_getenv) real_secure_getenv = (PFN_SEC_GETENV)dlsym(RTLD_NEXT, "secure_getenv");
226
227 if (platform_shim.use_fake_elevation) {
228 return NULL;
229 } else {
230 return real_secure_getenv(name);
231 }
232 }
233 #endif
234 #if defined(HAVE___SECURE_GETENV)
__SECURE_GETENV_FUNC_NAME(const char * name)235 FRAMEWORK_EXPORT char* __SECURE_GETENV_FUNC_NAME(const char* name) {
236 if (!real__secure_getenv) real__secure_getenv = (PFN_SEC_GETENV)dlsym(RTLD_NEXT, "__secure_getenv");
237
238 if (platform_shim.use_fake_elevation) {
239 return NULL;
240 } else {
241 return real__secure_getenv(name);
242 }
243 }
244 #endif
245
246 /* Shiming functions on apple is limited by the linker prefering to not use functions in the
247 * executable in loaded dylibs. By adding an interposer, we redirect the linker to use our
248 * version of the function over the real one, thus shimming the system function.
249 */
250 #if defined(__APPLE__)
251 #define MACOS_ATTRIB __attribute__((section("__DATA,__interpose")))
252 #define VOIDP_CAST(_func) reinterpret_cast<const void*>(&_func)
253
254 struct Interposer {
255 const void* shim_function;
256 const void* underlying_function;
257 };
258
259 __attribute__((used)) static Interposer _interpose_opendir MACOS_ATTRIB = {VOIDP_CAST(my_opendir), VOIDP_CAST(opendir)};
260 // don't intercept readdir as it crashes when using ASAN with macOS
261 // __attribute__((used)) static Interposer _interpose_readdir MACOS_ATTRIB = {VOIDP_CAST(my_readdir), VOIDP_CAST(readdir)};
262 __attribute__((used)) static Interposer _interpose_closedir MACOS_ATTRIB = {VOIDP_CAST(my_closedir), VOIDP_CAST(closedir)};
263 __attribute__((used)) static Interposer _interpose_access MACOS_ATTRIB = {VOIDP_CAST(my_access), VOIDP_CAST(access)};
264 __attribute__((used)) static Interposer _interpose_fopen MACOS_ATTRIB = {VOIDP_CAST(my_fopen), VOIDP_CAST(fopen)};
265 __attribute__((used)) static Interposer _interpose_euid MACOS_ATTRIB = {VOIDP_CAST(my_geteuid), VOIDP_CAST(geteuid)};
266 __attribute__((used)) static Interposer _interpose_egid MACOS_ATTRIB = {VOIDP_CAST(my_getegid), VOIDP_CAST(getegid)};
267 #if defined(HAVE_SECURE_GETENV)
268 __attribute__((used)) static Interposer _interpose_secure_getenv MACOS_ATTRIB = {VOIDP_CAST(my_secure_getenv),
269 VOIDP_CAST(secure_getenv)};
270 #endif
271 #if defined(HAVE___SECURE_GETENV)
272 __attribute__((used)) static Interposer _interpose__secure_getenv MACOS_ATTRIB = {VOIDP_CAST(my__secure_getenv),
273 VOIDP_CAST(__secure_getenv)};
274 #endif
275 #endif
276 } // extern "C"
277