Reintegrate socket duplication via WSADuplicateSocket/WSASocket.
* autoload.cc (WSADuplicateSocketW): Define. (WSASocketW): Define. * dtable.cc (dtable::release): Call dec_need_fixup_before if necessary. (dtable::fixup_before_fork): New function. (dtable::fixup_before_exec): New function. * dtable.h (class dtable): Add member cnt_need_fixup_before. Add declarations for above new functions. (dtable::dec_need_fixup_before): New inline method. (dtable::inc_need_fixup_before): New inline method. (dtable::need_fixup_before): New inline method. * fhandler.h (fhandler_base::fixup_before_fork_exec): New virtual method. (fhandler_base::need_fixup_before): New virtual method. (class fhandler_socket): Add member prot_info_ptr. (fhandler_socket::init_fixup_before): Declare. (fhandler_socket::need_fixup_before): New inline method. (fhandler_socket::fixup_before_fork_exec): Declare. (fhandler_socket::fixup_after_exec): Declare. * fhandler_socket.cc (fhandler_socket::fhandler_socket): Initialize prot_info_ptr to NULL. (fhandler_socket::~fhandler_socket): Free prot_info_ptr conditionally. (fhandler_socket::init_fixup_before): New method. (fhandler_socket::fixup_before_fork_exec): Ditto. (fhandler_socket::fixup_after_fork): Use WSASocketW to duplicate socket if necessary. (fhandler_socket::fixup_after_exec): New method. (fhandler_socket::dup): Use fixup_before_fork_exec/fixup_after_fork to duplicate socket if necessary. * fork.cc (frok::parent): Start child suspended if some fhandler needs fixup before fork. If so, call dtable::fixup_before_fork after CreateProcess and resume child. * net.cc (fdsock): Try to find out if socket needs fixup before and initialize socket accordingly. Add HUGE comment to explain what happens and why. * spawn.cc (spawn_guts): Start child suspended if some fhandler needs fixup before exec. If so, call dtable::fixup_before_exec after CreateProcess.
This commit is contained in:
parent
88242190ec
commit
b14f53a8ec
|
@ -1,3 +1,44 @@
|
|||
2009-11-17 Corinna Vinschen <corinna@vinschen.de>
|
||||
|
||||
Reintegrate socket duplication via WSADuplicateSocket/WSASocket.
|
||||
* autoload.cc (WSADuplicateSocketW): Define.
|
||||
(WSASocketW): Define.
|
||||
* dtable.cc (dtable::release): Call dec_need_fixup_before if necessary.
|
||||
(dtable::fixup_before_fork): New function.
|
||||
(dtable::fixup_before_exec): New function.
|
||||
* dtable.h (class dtable): Add member cnt_need_fixup_before. Add
|
||||
declarations for above new functions.
|
||||
(dtable::dec_need_fixup_before): New inline method.
|
||||
(dtable::inc_need_fixup_before): New inline method.
|
||||
(dtable::need_fixup_before): New inline method.
|
||||
* fhandler.h (fhandler_base::fixup_before_fork_exec): New virtual
|
||||
method.
|
||||
(fhandler_base::need_fixup_before): New virtual method.
|
||||
(class fhandler_socket): Add member prot_info_ptr.
|
||||
(fhandler_socket::init_fixup_before): Declare.
|
||||
(fhandler_socket::need_fixup_before): New inline method.
|
||||
(fhandler_socket::fixup_before_fork_exec): Declare.
|
||||
(fhandler_socket::fixup_after_exec): Declare.
|
||||
* fhandler_socket.cc (fhandler_socket::fhandler_socket): Initialize
|
||||
prot_info_ptr to NULL.
|
||||
(fhandler_socket::~fhandler_socket): Free prot_info_ptr conditionally.
|
||||
(fhandler_socket::init_fixup_before): New method.
|
||||
(fhandler_socket::fixup_before_fork_exec): Ditto.
|
||||
(fhandler_socket::fixup_after_fork): Use WSASocketW to duplicate
|
||||
socket if necessary.
|
||||
(fhandler_socket::fixup_after_exec): New method.
|
||||
(fhandler_socket::dup): Use fixup_before_fork_exec/fixup_after_fork
|
||||
to duplicate socket if necessary.
|
||||
* fork.cc (frok::parent): Start child suspended if some fhandler
|
||||
needs fixup before fork. If so, call dtable::fixup_before_fork after
|
||||
CreateProcess and resume child.
|
||||
* net.cc (fdsock): Try to find out if socket needs fixup before and
|
||||
initialize socket accordingly. Add HUGE comment to explain what happens
|
||||
and why.
|
||||
* spawn.cc (spawn_guts): Start child suspended if some fhandler needs
|
||||
fixup before exec. If so, call dtable::fixup_before_exec after
|
||||
CreateProcess.
|
||||
|
||||
2009-11-16 Eric Blake <ebb9@byu.net>
|
||||
|
||||
* environ.cc (setenv): Detect invalid argument.
|
||||
|
|
|
@ -387,6 +387,7 @@ LoadDLLfunc (setsockopt, 20, ws2_32)
|
|||
LoadDLLfunc (shutdown, 8, ws2_32)
|
||||
LoadDLLfunc (socket, 12, ws2_32)
|
||||
LoadDLLfunc (WSAAsyncSelect, 16, ws2_32)
|
||||
LoadDLLfunc (WSADuplicateSocketW, 12, ws2_32)
|
||||
LoadDLLfunc (WSAEnumNetworkEvents, 12, ws2_32)
|
||||
LoadDLLfunc (WSAEventSelect, 12, ws2_32)
|
||||
LoadDLLfunc (WSAGetLastError, 0, ws2_32)
|
||||
|
@ -396,6 +397,7 @@ LoadDLLfunc (WSARecvFrom, 36, ws2_32)
|
|||
LoadDLLfunc (WSASendMsg, 24, ws2_32)
|
||||
LoadDLLfunc (WSASendTo, 36, ws2_32)
|
||||
LoadDLLfunc (WSASetLastError, 4, ws2_32)
|
||||
LoadDLLfunc (WSASocketW, 24, ws2_32)
|
||||
// LoadDLLfunc (WSAStartup, 8, ws2_32)
|
||||
LoadDLLfunc (WSAWaitForMultipleEvents, 20, ws2_32)
|
||||
|
||||
|
|
|
@ -248,6 +248,8 @@ dtable::release (int fd)
|
|||
{
|
||||
if (!not_open (fd))
|
||||
{
|
||||
if (fds[fd]->need_fixup_before ())
|
||||
dec_need_fixup_before ();
|
||||
fhandler_base *arch = fds[fd]->archetype;
|
||||
delete fds[fd];
|
||||
if (arch && !arch->usecount)
|
||||
|
@ -1020,3 +1022,31 @@ handle_to_fn (HANDLE h, char *posix_fn)
|
|||
strcpy (posix_fn, unknown_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
dtable::fixup_before_fork (DWORD target_proc_id)
|
||||
{
|
||||
lock ();
|
||||
fhandler_base *fh;
|
||||
for (size_t i = 0; i < size; i++)
|
||||
if ((fh = fds[i]) != NULL)
|
||||
{
|
||||
debug_printf ("fd %d (%s)", i, fh->get_name ());
|
||||
fh->fixup_before_fork_exec (target_proc_id);
|
||||
}
|
||||
unlock ();
|
||||
}
|
||||
void
|
||||
dtable::fixup_before_exec (DWORD target_proc_id)
|
||||
{
|
||||
lock ();
|
||||
fhandler_base *fh;
|
||||
for (size_t i = 0; i < size; i++)
|
||||
if ((fh = fds[i]) != NULL && !fh->close_on_exec ())
|
||||
{
|
||||
debug_printf ("fd %d (%s)", i, fh->get_name ());
|
||||
fh->fixup_before_fork_exec (target_proc_id);
|
||||
}
|
||||
unlock ();
|
||||
}
|
||||
|
||||
|
|
|
@ -31,14 +31,22 @@ class dtable
|
|||
unsigned farchetype;
|
||||
static const int initial_archetype_size = 8;
|
||||
int first_fd_for_open;
|
||||
int cnt_need_fixup_before;
|
||||
void lock () {lock_process::locker.acquire ();}
|
||||
void unlock () {lock_process::locker.release ();}
|
||||
public:
|
||||
size_t size;
|
||||
|
||||
dtable () : archetypes (NULL), narchetypes (0), farchetype (0), first_fd_for_open(3) {}
|
||||
dtable () : archetypes (NULL), narchetypes (0), farchetype (0), first_fd_for_open(3), cnt_need_fixup_before(0) {}
|
||||
void init () {first_fd_for_open = 3;}
|
||||
|
||||
void dec_need_fixup_before ()
|
||||
{ if (cnt_need_fixup_before > 0) --cnt_need_fixup_before; }
|
||||
void inc_need_fixup_before ()
|
||||
{ cnt_need_fixup_before++; }
|
||||
bool need_fixup_before ()
|
||||
{ return cnt_need_fixup_before > 0; }
|
||||
|
||||
void move_fd (int, int);
|
||||
int vfork_child_dup ();
|
||||
void vfork_parent_restore ();
|
||||
|
@ -73,6 +81,8 @@ public:
|
|||
fhandler_base *find_archetype (device& dev);
|
||||
fhandler_base **add_archetype ();
|
||||
void delete_archetype (fhandler_base *);
|
||||
void fixup_before_exec (DWORD win_proc_id);
|
||||
void fixup_before_fork (DWORD win_proc_id);
|
||||
friend void dtable_init ();
|
||||
friend void __stdcall close_all_files (bool);
|
||||
friend class fhandler_disk_file;
|
||||
|
|
|
@ -226,6 +226,7 @@ class fhandler_base
|
|||
return close_on_exec () ? &sec_none_nih : &sec_none;
|
||||
}
|
||||
|
||||
virtual int fixup_before_fork_exec (DWORD) { return 0; }
|
||||
virtual void fixup_after_fork (HANDLE);
|
||||
virtual void fixup_after_exec ();
|
||||
void create_read_state (LONG n)
|
||||
|
@ -270,6 +271,7 @@ class fhandler_base
|
|||
|
||||
/* fixup fd possibly non-inherited handles after fork */
|
||||
bool fork_fixup (HANDLE, HANDLE &, const char *);
|
||||
virtual bool need_fixup_before () const {return false;}
|
||||
|
||||
virtual int open (int, mode_t = 0);
|
||||
int open_fs (int, mode_t = 0);
|
||||
|
@ -463,6 +465,12 @@ class fhandler_socket: public fhandler_base
|
|||
void rmem (int nrmem) { _rmem = nrmem; }
|
||||
void wmem (int nwmem) { _wmem = nwmem; }
|
||||
|
||||
private:
|
||||
struct _WSAPROTOCOL_INFOW *prot_info_ptr;
|
||||
public:
|
||||
void init_fixup_before ();
|
||||
bool need_fixup_before () const {return prot_info_ptr != NULL;}
|
||||
|
||||
private:
|
||||
char *sun_path;
|
||||
char *peer_sun_path;
|
||||
|
@ -524,7 +532,9 @@ class fhandler_socket: public fhandler_base
|
|||
int dup (fhandler_base *child);
|
||||
|
||||
void set_close_on_exec (bool val);
|
||||
int fixup_before_fork_exec (DWORD);
|
||||
void fixup_after_fork (HANDLE);
|
||||
void fixup_after_exec ();
|
||||
char *get_proc_fd_name (char *buf);
|
||||
|
||||
select_record *select_read (select_stuff *);
|
||||
|
|
|
@ -151,6 +151,7 @@ fhandler_socket::fhandler_socket () :
|
|||
wsock_events (NULL),
|
||||
wsock_mtx (NULL),
|
||||
wsock_evt (NULL),
|
||||
prot_info_ptr (NULL),
|
||||
sun_path (NULL),
|
||||
peer_sun_path (NULL),
|
||||
status ()
|
||||
|
@ -160,6 +161,8 @@ fhandler_socket::fhandler_socket () :
|
|||
|
||||
fhandler_socket::~fhandler_socket ()
|
||||
{
|
||||
if (prot_info_ptr)
|
||||
cfree (prot_info_ptr);
|
||||
if (sun_path)
|
||||
cfree (sun_path);
|
||||
if (peer_sun_path)
|
||||
|
@ -631,12 +634,62 @@ fhandler_socket::release_events ()
|
|||
NtClose (wsock_mtx);
|
||||
}
|
||||
|
||||
/* Called from net.cc:fdsock() if a freshly created socket is not
|
||||
inheritable. In that case we use fixup_before_fork_exec. See
|
||||
the comment in fdsock() for a description of the problem. */
|
||||
void
|
||||
fhandler_socket::init_fixup_before ()
|
||||
{
|
||||
prot_info_ptr = (LPWSAPROTOCOL_INFOW)
|
||||
cmalloc_abort (HEAP_BUF, sizeof (WSAPROTOCOL_INFOW));
|
||||
cygheap->fdtab.inc_need_fixup_before ();
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_socket::fixup_before_fork_exec (DWORD win_pid)
|
||||
{
|
||||
SOCKET ret = WSADuplicateSocketW (get_socket (), win_pid, prot_info_ptr);
|
||||
if (ret)
|
||||
set_winsock_errno ();
|
||||
else
|
||||
debug_printf ("WSADuplicateSocket succeeded");
|
||||
return (int) ret;
|
||||
}
|
||||
|
||||
void
|
||||
fhandler_socket::fixup_after_fork (HANDLE parent)
|
||||
{
|
||||
fork_fixup (parent, wsock_mtx, "wsock_mtx");
|
||||
fork_fixup (parent, wsock_evt, "wsock_evt");
|
||||
fhandler_base::fixup_after_fork (parent);
|
||||
|
||||
if (!need_fixup_before ())
|
||||
{
|
||||
fhandler_base::fixup_after_fork (parent);
|
||||
return;
|
||||
}
|
||||
|
||||
SOCKET new_sock = WSASocketW (FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
|
||||
FROM_PROTOCOL_INFO, prot_info_ptr, 0, 0);
|
||||
if (new_sock == INVALID_SOCKET)
|
||||
{
|
||||
set_winsock_errno ();
|
||||
set_io_handle ((HANDLE) INVALID_SOCKET);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Even though the original socket was not inheritable, the duplicated
|
||||
socket is potentially inheritable again. */
|
||||
SetHandleInformation ((HANDLE) new_sock, HANDLE_FLAG_INHERIT, 0);
|
||||
set_io_handle ((HANDLE) new_sock);
|
||||
debug_printf ("WSASocket succeeded");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fhandler_socket::fixup_after_exec ()
|
||||
{
|
||||
if (need_fixup_before () && !close_on_exec ())
|
||||
fixup_after_fork (NULL);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -679,13 +732,33 @@ fhandler_socket::dup (fhandler_base *child)
|
|||
}
|
||||
}
|
||||
fhs->connect_state (connect_state ());
|
||||
int ret = fhandler_base::dup (child);
|
||||
if (ret)
|
||||
|
||||
if (!need_fixup_before ())
|
||||
{
|
||||
NtClose (fhs->wsock_evt);
|
||||
NtClose (fhs->wsock_mtx);
|
||||
int ret = fhandler_base::dup (child);
|
||||
if (ret)
|
||||
{
|
||||
NtClose (fhs->wsock_evt);
|
||||
NtClose (fhs->wsock_mtx);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
|
||||
cygheap->user.deimpersonate ();
|
||||
fhs->init_fixup_before ();
|
||||
fhs->set_io_handle (get_io_handle ());
|
||||
if (!fhs->fixup_before_fork_exec (GetCurrentProcessId ()))
|
||||
{
|
||||
cygheap->user.reimpersonate ();
|
||||
fhs->fixup_after_fork (hMainProc);
|
||||
if (fhs->get_io_handle() != (HANDLE) INVALID_SOCKET)
|
||||
return 0;
|
||||
}
|
||||
cygheap->user.reimpersonate ();
|
||||
cygheap->fdtab.dec_need_fixup_before ();
|
||||
NtClose (fhs->wsock_evt);
|
||||
NtClose (fhs->wsock_mtx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __stdcall
|
||||
|
|
|
@ -302,6 +302,13 @@ frok::parent (volatile char * volatile stack_here)
|
|||
else
|
||||
c_flags |= DETACHED_PROCESS;
|
||||
|
||||
/* Some file types (currently only sockets) need extra effort in the
|
||||
parent after CreateProcess and before copying the datastructures
|
||||
to the child. So we have to start the child in suspend state,
|
||||
unfortunately, to avoid a race condition. */
|
||||
if (cygheap->fdtab.need_fixup_before ())
|
||||
c_flags |= CREATE_SUSPENDED;
|
||||
|
||||
/* Remember if we need to load dynamically linked dlls.
|
||||
We do this here so that this information will be available
|
||||
in the parent and, when the stack is copied, in the child. */
|
||||
|
@ -369,6 +376,12 @@ frok::parent (volatile char * volatile stack_here)
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
if (cygheap->fdtab.need_fixup_before ())
|
||||
{
|
||||
cygheap->fdtab.fixup_before_fork (pi.dwProcessId);
|
||||
ResumeThread (pi.hThread);
|
||||
}
|
||||
|
||||
CloseHandle (pi.hThread);
|
||||
|
||||
/* Protect the handle but name it similarly to the way it will
|
||||
|
|
|
@ -495,6 +495,34 @@ fdsock (cygheap_fdmanip& fd, const device *dev, SOCKET soc)
|
|||
fd->uninterruptible_io (true);
|
||||
debug_printf ("fd %d, name '%s', soc %p", (int) fd, dev->name, soc);
|
||||
|
||||
/* Usually sockets are inheritable IFS objects. Unfortunately some virus
|
||||
scanners or other network-oriented software replace normal sockets
|
||||
with their own kind, which is running through a filter driver.
|
||||
|
||||
The result is that these new sockets are not normal kernel objects
|
||||
anymore. They are typically not marked as inheritable, nor are they
|
||||
IFS handles, as normal OS sockets are. They are in fact not inheritable
|
||||
to child processes, and subsequent socket calls in the child process
|
||||
will fail with error 10038, WSAENOTSOCK. And worse, while DuplicateHandle
|
||||
on these sockets mostly works in the process which created the socket,
|
||||
DuplicateHandle does quite often not work anymore in a child process.
|
||||
It does not help to mark them inheritable via SetHandleInformation.
|
||||
|
||||
The only way to make these sockets usable in child processes is to
|
||||
duplicate them via WSADuplicateSocket/WSASocket calls. This requires
|
||||
some incredible amount of extra processing so we only do this on
|
||||
affected systems. If we recognize a non-inheritable socket, or if
|
||||
the XP1_IFS_HANDLES flag is not set in a call to WSADuplicateSocket,
|
||||
we switch to inheritance/dup via WSADuplicateSocket/WSASocket for
|
||||
that socket. */
|
||||
DWORD flags;
|
||||
WSAPROTOCOL_INFOW wpi;
|
||||
if (!GetHandleInformation ((HANDLE) soc, &flags)
|
||||
|| !(flags & HANDLE_FLAG_INHERIT)
|
||||
|| WSADuplicateSocketW (soc, GetCurrentProcessId (), &wpi)
|
||||
|| !(wpi.dwServiceFlags1 & XP1_IFS_HANDLES))
|
||||
((fhandler_socket *) fd)->init_fixup_before ();
|
||||
|
||||
/* Raise default buffer sizes (instead of WinSock default 8K).
|
||||
|
||||
64K appear to have the best size/performance ratio for a default
|
||||
|
|
|
@ -529,7 +529,8 @@ spawn_guts (const char *prog_arg, const char *const *argv,
|
|||
and before copying the datastructures to the child. So we have to start
|
||||
the child in suspend state, unfortunately, to avoid a race condition. */
|
||||
if (!newargv.win16_exe
|
||||
&& (!ch.iscygwin () || mode != _P_OVERLAY))
|
||||
&& (!ch.iscygwin () || mode != _P_OVERLAY
|
||||
|| cygheap->fdtab.need_fixup_before ()))
|
||||
c_flags |= CREATE_SUSPENDED;
|
||||
|
||||
/* When ruid != euid we create the new process under the current original
|
||||
|
@ -656,6 +657,11 @@ loop:
|
|||
if (!(c_flags & CREATE_SUSPENDED))
|
||||
strace.write_childpid (ch, pi.dwProcessId);
|
||||
|
||||
/* Fixup the parent data structures if needed and resume the child's
|
||||
main thread. */
|
||||
if (cygheap->fdtab.need_fixup_before ())
|
||||
cygheap->fdtab.fixup_before_exec (pi.dwProcessId);
|
||||
|
||||
if (mode != _P_OVERLAY)
|
||||
cygpid = cygwin_pid (pi.dwProcessId);
|
||||
else
|
||||
|
|
Loading…
Reference in New Issue