|
38 | 38 | #include <io.h>
|
39 | 39 | #include <signal.h>
|
40 | 40 | #include <winioctl.h>
|
| 41 | +#include <winternl.h> |
41 | 42 |
|
42 | 43 | /* #include "config.h" */
|
43 | 44 |
|
@@ -156,9 +157,6 @@ static void translate_to_errno(void);
|
156 | 157 | START_EXTERN_C
|
157 | 158 | HANDLE w32_perldll_handle = INVALID_HANDLE_VALUE;
|
158 | 159 | char w32_module_name[MAX_PATH+1];
|
159 |
| -#ifdef WIN32_DYN_IOINFO_SIZE |
160 |
| -Size_t w32_ioinfo_size;/* avoid 0 extend op b4 mul, otherwise could be a U8 */ |
161 |
| -#endif |
162 | 160 | END_EXTERN_C
|
163 | 161 |
|
164 | 162 | static OSVERSIONINFO g_osver = {0, 0, 0, 0, 0, ""};
|
@@ -3313,7 +3311,7 @@ win32_freopen(const char *path, const char *mode, FILE *stream)
|
3313 | 3311 | DllExport int
|
3314 | 3312 | win32_fclose(FILE *pf)
|
3315 | 3313 | {
|
3316 |
| - return my_fclose(pf); /* defined in win32sck.c */ |
| 3314 | + return fclose(pf); |
3317 | 3315 | }
|
3318 | 3316 |
|
3319 | 3317 | DllExport int
|
@@ -3913,13 +3911,10 @@ win32_open(const char *path, int flag, ...)
|
3913 | 3911 | return open(PerlDir_mapA(path), flag, pmode);
|
3914 | 3912 | }
|
3915 | 3913 |
|
3916 |
| -/* close() that understands socket */ |
3917 |
| -extern int my_close(int); /* in win32sck.c */ |
3918 |
| - |
3919 | 3914 | DllExport int
|
3920 | 3915 | win32_close(int fd)
|
3921 | 3916 | {
|
3922 |
| - return my_close(fd); |
| 3917 | + return _close(fd); |
3923 | 3918 | }
|
3924 | 3919 |
|
3925 | 3920 | DllExport int
|
@@ -5172,6 +5167,172 @@ ansify_path(void)
|
5172 | 5167 | win32_free(wide_path);
|
5173 | 5168 | }
|
5174 | 5169 |
|
| 5170 | +/* This hooks a function that is imported by the specified module. The hook is |
| 5171 | + * local to that module. */ |
| 5172 | +static bool |
| 5173 | +win32_hook_imported_function_in_module( |
| 5174 | + HMODULE module, LPCSTR fun_name, FARPROC hook_ptr |
| 5175 | +) |
| 5176 | +{ |
| 5177 | + ULONG_PTR image_base = (ULONG_PTR)module; |
| 5178 | + PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)image_base; |
| 5179 | + PIMAGE_NT_HEADERS nt_headers |
| 5180 | + = (PIMAGE_NT_HEADERS)(image_base + dos_header->e_lfanew); |
| 5181 | + PIMAGE_OPTIONAL_HEADER opt_header = &nt_headers->OptionalHeader; |
| 5182 | + |
| 5183 | + PIMAGE_DATA_DIRECTORY data_dir = opt_header->DataDirectory; |
| 5184 | + DWORD data_dir_len = opt_header->NumberOfRvaAndSizes; |
| 5185 | + |
| 5186 | + BOOL is_idt_present = data_dir_len > IMAGE_DIRECTORY_ENTRY_IMPORT |
| 5187 | + && data_dir[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress != 0; |
| 5188 | + |
| 5189 | + if (!is_idt_present) |
| 5190 | + return FALSE; |
| 5191 | + |
| 5192 | + BOOL found = FALSE; |
| 5193 | + |
| 5194 | + /* Import Directory Table */ |
| 5195 | + PIMAGE_IMPORT_DESCRIPTOR idt = (PIMAGE_IMPORT_DESCRIPTOR)( |
| 5196 | + image_base + data_dir[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress |
| 5197 | + ); |
| 5198 | + |
| 5199 | + for (; idt->Name != 0; ++idt) { |
| 5200 | + /* Import Lookup Table */ |
| 5201 | + PIMAGE_THUNK_DATA ilt |
| 5202 | + = (PIMAGE_THUNK_DATA)(image_base + idt->OriginalFirstThunk); |
| 5203 | + /* Import Address Table */ |
| 5204 | + PIMAGE_THUNK_DATA iat |
| 5205 | + = (PIMAGE_THUNK_DATA)(image_base + idt->FirstThunk); |
| 5206 | + |
| 5207 | + ULONG_PTR address_of_data; |
| 5208 | + for (; address_of_data = ilt->u1.AddressOfData; ++ilt, ++iat) { |
| 5209 | + /* Ordinal imports are quite rare, so skipping them will most likely |
| 5210 | + * not cause any problems. */ |
| 5211 | + BOOL is_ordinal |
| 5212 | + = address_of_data >> ((sizeof(address_of_data) * 8) - 1); |
| 5213 | + |
| 5214 | + if (is_ordinal) |
| 5215 | + continue; |
| 5216 | + |
| 5217 | + LPCSTR name = ( |
| 5218 | + (PIMAGE_IMPORT_BY_NAME)(image_base + address_of_data) |
| 5219 | + )->Name; |
| 5220 | + |
| 5221 | + if (strEQ(name, fun_name)) { |
| 5222 | + DWORD old_protect = 0; |
| 5223 | + BOOL succ = VirtualProtect( |
| 5224 | + &iat->u1.Function, sizeof(iat->u1.Function), PAGE_READWRITE, |
| 5225 | + &old_protect |
| 5226 | + ); |
| 5227 | + if (!succ) |
| 5228 | + return FALSE; |
| 5229 | + |
| 5230 | + iat->u1.Function = (ULONG_PTR)hook_ptr; |
| 5231 | + found = TRUE; |
| 5232 | + |
| 5233 | + VirtualProtect( |
| 5234 | + &iat->u1.Function, sizeof(iat->u1.Function), old_protect, |
| 5235 | + &old_protect |
| 5236 | + ); |
| 5237 | + break; |
| 5238 | + } |
| 5239 | + } |
| 5240 | + } |
| 5241 | + |
| 5242 | + return found; |
| 5243 | +} |
| 5244 | + |
| 5245 | +typedef NTSTATUS (NTAPI *pNtQueryInformationFile_t)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, ULONG); |
| 5246 | +pNtQueryInformationFile_t pNtQueryInformationFile = NULL; |
| 5247 | + |
| 5248 | +typedef BOOL (WINAPI *pCloseHandle)(HANDLE h); |
| 5249 | +static pCloseHandle CloseHandle_orig; |
| 5250 | + |
| 5251 | +/* CloseHandle() that supports sockets. CRT uses mutexes during file operations, |
| 5252 | + * so the lack of thread safety in this function isn't a problem. */ |
| 5253 | +static BOOL WINAPI |
| 5254 | +my_CloseHandle(HANDLE h) |
| 5255 | +{ |
| 5256 | + /* In theory, passing a non-socket handle to closesocket() is fine. It |
| 5257 | + * should return a WSAENOTSOCK error, which is easy to recover from. |
| 5258 | + * However, we should avoid doing that because it's not that simple in |
| 5259 | + * practice. For instance, it can deadlock on a handle to a stuck pipe (see: |
| 5260 | + * https://github.com/Perl/perl5/issues/19963). |
| 5261 | + * |
| 5262 | + * There's no foolproof way to tell if a handle is a socket (mostly because |
| 5263 | + * of the non-IFS sockets), but in some cases we can tell if a handle |
| 5264 | + * is definitely *not* a socket. |
| 5265 | + */ |
| 5266 | + |
| 5267 | + /* GetFileType() always returns FILE_TYPE_PIPE for sockets. */ |
| 5268 | + BOOL maybe_socket = (GetFileType(h) == FILE_TYPE_PIPE); |
| 5269 | + |
| 5270 | + if (maybe_socket && pNtQueryInformationFile) { |
| 5271 | + IO_STATUS_BLOCK isb; |
| 5272 | + struct { |
| 5273 | + ULONG name_len; |
| 5274 | + WCHAR name[100]; |
| 5275 | + } volume = {0}; |
| 5276 | + |
| 5277 | + /* There are many ways to tell a named pipe from a socket, but almost |
| 5278 | + * all of them can deadlock on a handle to a stuck pipe (like in the |
| 5279 | + * bug ticket mentioned above). According to my tests, |
| 5280 | + * FileVolumeNameInfomation is the only relevant function that doesn't |
| 5281 | + * suffer from this problem. |
| 5282 | + * |
| 5283 | + * It's undocumented and it requires Windows 10, so on older systems |
| 5284 | + * we always pass pipes to closesocket(). |
| 5285 | + */ |
| 5286 | + NTSTATUS s = pNtQueryInformationFile( |
| 5287 | + h, &isb, &volume, sizeof(volume), 58 /* FileVolumeNameInformation */ |
| 5288 | + ); |
| 5289 | + if (NT_SUCCESS(s)) { |
| 5290 | + maybe_socket = (_wcsnicmp( |
| 5291 | + volume.name, L"\\Device\\NamedPipe", C_ARRAY_LENGTH(volume.name) |
| 5292 | + ) != 0); |
| 5293 | + } |
| 5294 | + } |
| 5295 | + |
| 5296 | + if (maybe_socket) |
| 5297 | + if (closesocket((SOCKET)h) == 0) |
| 5298 | + return TRUE; |
| 5299 | + else if (WSAGetLastError() != WSAENOTSOCK) |
| 5300 | + return FALSE; |
| 5301 | + |
| 5302 | + return CloseHandle_orig(h); |
| 5303 | +} |
| 5304 | + |
| 5305 | +/* Hook CloseHandle() inside CRT so its functions like _close() or |
| 5306 | + * _dup2() can close sockets properly. */ |
| 5307 | +static void |
| 5308 | +win32_hook_closehandle_in_crt() |
| 5309 | +{ |
| 5310 | + /* Get the handle to the CRT module basing on the address of _close() |
| 5311 | + * function. */ |
| 5312 | + HMODULE crt_handle; |
| 5313 | + BOOL succ = GetModuleHandleExA( |
| 5314 | + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
| 5315 | + | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)_close, |
| 5316 | + &crt_handle |
| 5317 | + ); |
| 5318 | + if (!succ) |
| 5319 | + return; |
| 5320 | + |
| 5321 | + CloseHandle_orig = (pCloseHandle)GetProcAddress( |
| 5322 | + GetModuleHandleA("kernel32.dll"), "CloseHandle" |
| 5323 | + ); |
| 5324 | + if (!CloseHandle_orig) |
| 5325 | + return; |
| 5326 | + |
| 5327 | + win32_hook_imported_function_in_module( |
| 5328 | + crt_handle, "CloseHandle", (FARPROC)my_CloseHandle |
| 5329 | + ); |
| 5330 | + |
| 5331 | + pNtQueryInformationFile = (pNtQueryInformationFile_t)GetProcAddress( |
| 5332 | + GetModuleHandleA("ntdll.dll"), "NtQueryInformationFile" |
| 5333 | + ); |
| 5334 | +} |
| 5335 | + |
5175 | 5336 | void
|
5176 | 5337 | Perl_win32_init(int *argcp, char ***argvp)
|
5177 | 5338 | {
|
@@ -5208,17 +5369,7 @@ Perl_win32_init(int *argcp, char ***argvp)
|
5208 | 5369 | g_osver.dwOSVersionInfoSize = sizeof(g_osver);
|
5209 | 5370 | GetVersionEx(&g_osver);
|
5210 | 5371 |
|
5211 |
| -#ifdef WIN32_DYN_IOINFO_SIZE |
5212 |
| - { |
5213 |
| - Size_t ioinfo_size = _msize((void*)__pioinfo[0]);; |
5214 |
| - if((SSize_t)ioinfo_size <= 0) { /* -1 is err */ |
5215 |
| - fprintf(stderr, "panic: invalid size for ioinfo\n"); /* no interp */ |
5216 |
| - exit(1); |
5217 |
| - } |
5218 |
| - ioinfo_size /= IOINFO_ARRAY_ELTS; |
5219 |
| - w32_ioinfo_size = ioinfo_size; |
5220 |
| - } |
5221 |
| -#endif |
| 5372 | + win32_hook_closehandle_in_crt(); |
5222 | 5373 |
|
5223 | 5374 | ansify_path();
|
5224 | 5375 |
|
|
0 commit comments