Skip to content

Commit c192bf8

Browse files
committed
Preserve Windows socket mappings in dup2
1 parent 91d2241 commit c192bf8

2 files changed

Lines changed: 208 additions & 21 deletions

File tree

‎graalpython/com.oracle.graal.python.test/src/tests/test_posix.py‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def posix_module_backend(self):
5757
import io
5858
import glob
5959
import shutil
60+
import socket
6061
from contextlib import contextmanager
6162

6263

@@ -401,6 +402,81 @@ def assert_crt_error(operation, expected_errno):
401402
finally:
402403
server.close()
403404

405+
@unittest.skipUnless(sys.platform == 'win32' and __graalpython__.posix_module_backend() == 'native',
406+
'requires the Windows native POSIX backend')
407+
def test_windows_native_dup2_socket(self):
408+
source = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
409+
target = os.open(os.devnull, os.O_RDONLY)
410+
try:
411+
source.bind(('127.0.0.1', 0))
412+
self.assertEqual(target, os.dup2(source.fileno(), target))
413+
duplicate = socket.socket(fileno=target)
414+
target = -1
415+
with duplicate:
416+
self.assertEqual(source.getsockname(), duplicate.getsockname())
417+
finally:
418+
source.close()
419+
if target >= 0:
420+
os.close(target)
421+
422+
@unittest.skipUnless(sys.platform == 'win32' and __graalpython__.posix_module_backend() == 'native',
423+
'requires the Windows native POSIX backend')
424+
def test_windows_native_dup2_file_over_socket(self):
425+
read_fd, write_fd = os.pipe()
426+
target = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
427+
target_fd = target.detach()
428+
try:
429+
self.assertEqual(target_fd, os.dup2(read_fd, target_fd))
430+
os.close(read_fd)
431+
read_fd = -1
432+
os.write(write_fd, b'x')
433+
self.assertEqual(b'x', os.read(target_fd, 1))
434+
finally:
435+
if read_fd >= 0:
436+
os.close(read_fd)
437+
os.close(write_fd)
438+
os.close(target_fd)
439+
440+
@unittest.skipUnless(sys.platform == 'win32' and __graalpython__.posix_module_backend() == 'native',
441+
'requires the Windows native POSIX backend')
442+
def test_windows_native_dup2_socket_over_socket(self):
443+
source = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
444+
target = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
445+
target.bind(('127.0.0.1', 0))
446+
target_fd = target.detach()
447+
try:
448+
source.bind(('127.0.0.1', 0))
449+
self.assertEqual(target_fd, os.dup2(source.fileno(), target_fd))
450+
duplicate = socket.socket(fileno=target_fd)
451+
target_fd = -1
452+
with duplicate:
453+
self.assertEqual(source.getsockname(), duplicate.getsockname())
454+
finally:
455+
source.close()
456+
if target_fd >= 0:
457+
os.close(target_fd)
458+
459+
@unittest.skipUnless(sys.platform == 'win32' and __graalpython__.posix_module_backend() == 'native',
460+
'requires the Windows native POSIX backend')
461+
def test_windows_native_dup2_socket_to_self_non_inheritable(self):
462+
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
463+
fd = sock.fileno()
464+
os.set_inheritable(fd, True)
465+
self.assertEqual(fd, os.dup2(fd, fd, inheritable=False))
466+
self.assertFalse(os.get_inheritable(fd))
467+
sock.getsockname()
468+
469+
@unittest.skipUnless(sys.platform == 'win32' and __graalpython__.posix_module_backend() == 'native',
470+
'requires the Windows native POSIX backend')
471+
def test_windows_native_dup_nonblocking_socket(self):
472+
source = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
473+
source.setblocking(False)
474+
try:
475+
with socket.socket(fileno=os.dup(source.fileno())) as duplicate:
476+
self.assertFalse(duplicate.getblocking())
477+
finally:
478+
source.close()
479+
404480
def test_fd_converter(self):
405481
class MyInt(int):
406482
def fileno(self): return 0

‎graalpython/python-libposix/src/posix.c‎

Lines changed: 132 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include <winsock2.h>
4949
#include <ws2tcpip.h>
5050
#include <afunix.h>
51+
#include <assert.h>
5152
#include <windows.h>
5253
#include <direct.h>
5354
#include <errno.h>
@@ -440,6 +441,20 @@ static SOCKET win_socket_from_fd(int32_t fd) {
440441
return result;
441442
}
442443

444+
static int win_socket_blocking_from_fd(int32_t fd) {
445+
int result = 1;
446+
ensure_socket_table();
447+
EnterCriticalSection(&win_socket_table_lock);
448+
for (int i = 0; i < WIN_SOCKET_TABLE_SIZE; i++) {
449+
if (win_socket_table[i].fd == fd) {
450+
result = win_socket_table[i].blocking;
451+
break;
452+
}
453+
}
454+
LeaveCriticalSection(&win_socket_table_lock);
455+
return result;
456+
}
457+
443458
static int win_socket_entry_index(int32_t fd) {
444459
int result = -1;
445460
ensure_socket_table();
@@ -454,7 +469,7 @@ static int win_socket_entry_index(int32_t fd) {
454469
return result;
455470
}
456471

457-
static int win_alloc_socket_fd(SOCKET s) {
472+
static int win_alloc_socket_fd(SOCKET s, int blocking) {
458473
int fd = _open("NUL", _O_RDONLY | _O_BINARY | _O_NOINHERIT);
459474
if (fd < 0) {
460475
capture_errno();
@@ -468,7 +483,7 @@ static int win_alloc_socket_fd(SOCKET s) {
468483
if (win_socket_table[i].fd < 0) {
469484
win_socket_table[i].fd = fd;
470485
win_socket_table[i].socket = s;
471-
win_socket_table[i].blocking = 1;
486+
win_socket_table[i].blocking = blocking;
472487
LeaveCriticalSection(&win_socket_table_lock);
473488
return fd;
474489
}
@@ -498,17 +513,106 @@ static SOCKET win_remove_socket_fd(int32_t fd) {
498513
return result;
499514
}
500515

501-
static int win_socket_dup(int32_t fd) {
502-
SOCKET s = win_socket_from_fd(fd);
516+
GP_EXPORT int32_t set_inheritable(int32_t fd, int32_t inheritable);
517+
518+
/* The caller must hold win_socket_table_lock. */
519+
static int win_replace_socket_fd_locked(int32_t fd, SOCKET s, int blocking, SOCKET *replaced) {
520+
assert(win_socket_table_lock.OwningThread == (HANDLE) (uintptr_t) GetCurrentThreadId());
521+
int free_index = -1;
522+
for (int i = 0; i < WIN_SOCKET_TABLE_SIZE; i++) {
523+
if (win_socket_table[i].fd == fd) {
524+
*replaced = win_socket_table[i].socket;
525+
win_socket_table[i].socket = s;
526+
win_socket_table[i].blocking = blocking;
527+
return 0;
528+
}
529+
if (free_index < 0 && win_socket_table[i].fd < 0) {
530+
free_index = i;
531+
}
532+
}
533+
if (free_index < 0) {
534+
set_posix_errno(EMFILE);
535+
return -1;
536+
}
537+
win_socket_table[free_index].fd = fd;
538+
win_socket_table[free_index].socket = s;
539+
win_socket_table[free_index].blocking = blocking;
540+
*replaced = INVALID_SOCKET;
541+
return 0;
542+
}
543+
544+
static SOCKET win_duplicate_socket(SOCKET s) {
503545
WSAPROTOCOL_INFOA protocol_info;
504546
if (WSADuplicateSocketA(s, GetCurrentProcessId(), &protocol_info) == SOCKET_ERROR) {
505-
return set_wsa_errno();
547+
set_wsa_errno();
548+
return INVALID_SOCKET;
506549
}
507550
SOCKET dup = WSASocketA(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &protocol_info, 0, WSA_FLAG_NO_HANDLE_INHERIT);
508551
if (dup == INVALID_SOCKET) {
509-
return set_wsa_errno();
552+
set_wsa_errno();
553+
return INVALID_SOCKET;
510554
}
511-
return win_alloc_socket_fd(dup);
555+
return dup;
556+
}
557+
558+
static int win_socket_dup(int32_t fd) {
559+
SOCKET s = win_socket_from_fd(fd);
560+
int blocking = win_socket_blocking_from_fd(fd);
561+
SOCKET dup = win_duplicate_socket(s);
562+
if (dup == INVALID_SOCKET) {
563+
return -1;
564+
}
565+
return win_alloc_socket_fd(dup, blocking);
566+
}
567+
568+
static int win_socket_dup2(int32_t oldfd, int32_t newfd, int32_t inheritable) {
569+
SOCKET dup = win_duplicate_socket(win_socket_from_fd(oldfd));
570+
if (dup == INVALID_SOCKET) {
571+
return -1;
572+
}
573+
int blocking = win_socket_blocking_from_fd(oldfd);
574+
575+
/*
576+
* Keep the table locked while replacing the dummy CRT fd. This ensures that
577+
* a full table is reported before _dup2 changes newfd and that the table
578+
* entry is updated atomically with respect to other table users.
579+
*/
580+
ensure_socket_table();
581+
EnterCriticalSection(&win_socket_table_lock);
582+
int table_has_slot = 0;
583+
for (int i = 0; i < WIN_SOCKET_TABLE_SIZE; i++) {
584+
if (win_socket_table[i].fd == newfd || win_socket_table[i].fd < 0) {
585+
table_has_slot = 1;
586+
break;
587+
}
588+
}
589+
if (!table_has_slot) {
590+
LeaveCriticalSection(&win_socket_table_lock);
591+
closesocket(dup);
592+
set_posix_errno(EMFILE);
593+
return -1;
594+
}
595+
596+
if (dup2_noraise(oldfd, newfd) != 0) {
597+
LeaveCriticalSection(&win_socket_table_lock);
598+
closesocket(dup);
599+
return -1;
600+
}
601+
602+
SOCKET replaced = INVALID_SOCKET;
603+
int replaced_result = win_replace_socket_fd_locked(newfd, dup, blocking, &replaced);
604+
LeaveCriticalSection(&win_socket_table_lock);
605+
if (replaced_result < 0) {
606+
closesocket(dup);
607+
return -1;
608+
}
609+
if (replaced != INVALID_SOCKET) {
610+
closesocket(replaced);
611+
}
612+
if (!inheritable) {
613+
return set_inheritable(newfd, 0);
614+
}
615+
return 0;
512616
}
513617

514618
static intptr_t get_osfhandle_noraise(int32_t fd) {
@@ -915,8 +1019,25 @@ GP_EXPORT int32_t call_dup(int32_t fd) {
9151019
}
9161020

9171021
GP_EXPORT int32_t call_dup2(int32_t oldfd, int32_t newfd, int32_t inheritable) {
1022+
if (oldfd == newfd) {
1023+
int result = dup2_noraise(oldfd, newfd);
1024+
if (result == 0 && !inheritable) {
1025+
result = set_inheritable(newfd, 0);
1026+
}
1027+
return result == 0 ? newfd : -1;
1028+
}
1029+
if (win_socket_entry_index(oldfd) >= 0) {
1030+
return win_socket_dup2(oldfd, newfd, inheritable) == 0 ? newfd : -1;
1031+
}
9181032
int result = dup2_noraise(oldfd, newfd);
919-
if (result == 0 && !inheritable) {
1033+
if (result != 0) {
1034+
return -1;
1035+
}
1036+
SOCKET stale_socket = win_remove_socket_fd(newfd);
1037+
if (stale_socket != INVALID_SOCKET) {
1038+
closesocket(stale_socket);
1039+
}
1040+
if (!inheritable) {
9201041
result = set_inheritable(newfd, 0);
9211042
}
9221043
return result == 0 ? newfd : -1;
@@ -1038,17 +1159,7 @@ GP_EXPORT int32_t call_fsync(int32_t fd) {
10381159
GP_EXPORT int32_t call_flock(int32_t fd, int32_t operation) { return unsupported(); }
10391160
GP_EXPORT int32_t call_fcntl_lock(int32_t fd, int32_t blocking, int32_t lockType, int32_t whence, int64_t start, int64_t length) { return unsupported(); }
10401161
GP_EXPORT int32_t get_blocking(int32_t fd) {
1041-
int result = 1;
1042-
ensure_socket_table();
1043-
EnterCriticalSection(&win_socket_table_lock);
1044-
for (int i = 0; i < WIN_SOCKET_TABLE_SIZE; i++) {
1045-
if (win_socket_table[i].fd == fd) {
1046-
result = win_socket_table[i].blocking;
1047-
break;
1048-
}
1049-
}
1050-
LeaveCriticalSection(&win_socket_table_lock);
1051-
return result;
1162+
return win_socket_blocking_from_fd(fd);
10521163
}
10531164

10541165
GP_EXPORT int32_t set_blocking(int32_t fd, int32_t blocking) {
@@ -1607,7 +1718,7 @@ GP_EXPORT int32_t call_socket(int32_t family, int32_t type, int32_t protocol) {
16071718
if (s == INVALID_SOCKET) {
16081719
return set_wsa_errno();
16091720
}
1610-
return win_alloc_socket_fd(s);
1721+
return win_alloc_socket_fd(s, 1);
16111722
}
16121723

16131724
GP_EXPORT int32_t call_accept(int32_t sockfd, int8_t *addr, int32_t *addr_len) {
@@ -1617,7 +1728,7 @@ GP_EXPORT int32_t call_accept(int32_t sockfd, int8_t *addr, int32_t *addr_len) {
16171728
return set_wsa_errno();
16181729
}
16191730
*addr_len = len;
1620-
return win_alloc_socket_fd(s);
1731+
return win_alloc_socket_fd(s, 1);
16211732
}
16221733

16231734
GP_EXPORT int32_t call_bind(int32_t sockfd, int8_t *addr, int32_t addr_len) { int r = bind(win_socket_from_fd(sockfd), (struct sockaddr *) addr, addr_len); return r == SOCKET_ERROR ? set_wsa_errno() : r; }

0 commit comments

Comments
 (0)